You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2018/03/15 14:35:00 UTC

[sling-org-apache-sling-scripting-esx] 01/10: SLING-6680 - new esx/Nashorn script engine, contributed by Senol Tas, thanks!

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-scripting-esx.git

commit 398819aa994b29f7cd9a66b4eb564a745e8e7f05
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Fri Mar 24 15:09:46 2017 +0000

    SLING-6680 - new esx/Nashorn script engine, contributed by Senol Tas, thanks!
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1788464 13f79535-47bb-0310-9956-ffa450edef68
---
 README.md                                          | 141 +++++
 pom.xml                                            | 218 +++++++
 .../sling/scripting/esx/EsxScriptEngine.java       |  73 +++
 .../scripting/esx/EsxScriptEngineFactory.java      | 101 ++++
 .../org/apache/sling/scripting/esx/Module.java     | 657 +++++++++++++++++++++
 .../apache/sling/scripting/esx/ModuleScript.java   |  96 +++
 .../org/apache/sling/scripting/esx/Require.java    |  29 +
 .../sling/scripting/esx/ScriptModuleCache.java     |  57 ++
 .../sling/scripting/esx/plugins/ConsoleLog.java    |  49 ++
 .../scripting/esx/plugins/SimpleResource.java      |  95 +++
 .../esx/plugins/SimpleResourceAdapterFactory.java  |  51 ++
 .../scripting/esx/services/ScriptModuleCache.java  |  57 ++
 .../esx/services/impl/RepositoryModuleCache.java   |  75 +++
 src/main/resources/libs/esx/demo/README.md         |  42 ++
 .../components/esx_modules/BlogComponent/index.js  |  82 +++
 .../components/esx_modules/calculator/index.js     |  22 +
 .../esx/demo/components/hbstemplates/content.html  |  17 +
 .../esx/demo/components/hbstemplates/layout.html   |  68 +++
 .../components/page/helper/calculator/index.js     |  22 +
 .../demo/components/page/helper/pathinfo/index.js  |  30 +
 .../libs/esx/demo/components/page/page.esx         |  46 ++
 .../demo/components/page/templates/listing.html    |  29 +
 .../esx/demo/components/page/templates/mobile.html |  17 +
 .../libs/esx/demo/components/post/post.esx         |  34 ++
 .../esx/demo/components/post/templates/detail.html |  17 +
 src/main/resources/libs/esx/demo/content/demo.xml  | 175 ++++++
 src/main/resources/libs/esx/demo/package.json      |  17 +
 .../resources/libs/esx/esx_modules/fs/index.js     |  16 +
 .../resources/libs/esx/esx_modules/path/index.js   | 210 +++++++
 .../resources/libs/esx/esx_modules/url/index.js    |  70 +++
 30 files changed, 2613 insertions(+)

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a7a8489
--- /dev/null
+++ b/README.md
@@ -0,0 +1,141 @@
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+# Apache Sling ESX Scripting Engine
+
+A Node JS (like) module loader for Apache Sling.
+
+## Description
+This module implements a Nashorn Apache Sling Script Engine for the "esx" extension.
+
+It requires a function named `render` in the `esx` script that processes the request.
+
+To activate this script engine you must first **enable Nashorn support** in the 
+`sling.properties` file of your Sling instance:
+
+```
+jre-1.8=jdk.nashorn.api.scripting;version\="0.0.0.1_008_JavaSE"
+```
+**attention**
+> currently this implementation only works with java version "1.8.0_92" and higher
+
+Once the bundle is active, you can try the engine with this minimal (and not very interesting) example:
+
+First create a node with some content:
+
+    curl -u admin:admin \
+      -F"sling:resourceType=foo" \
+	  -Ftitle="Hello ESX" \
+	  -Ftext="Here's some example text" \
+	  http://localhost:8080/apps/foo
+	  
+Then create an ESX script to render it:
+
+    $ cat << EOF > /tmp/foo.esx
+    var foo = {
+      render: function () {
+        var output  = "<h1>" + currentNode.properties.title + "</h1>";             
+        output += currentNode.properties.text;
+        return output;     
+      }
+    }  
+    module.exports = foo;
+    EOF
+	
+    $ curl -u admin:admin -T /tmp/foo.esx http://localhost:8080/apps/foo/foo.esx
+   
+    $ curl http://localhost:8080/apps/foo.html
+    <h1>Hello ESX</h1>Here's some example text
+  	  
+
+An ESX file is a regular java script file. 
+
+The NodeJS module resolution (https://nodejs.org/api/modules.html) is implemented to give access to the
+rich collection of Node modules.
+
+There's currently no priority handling of global modules.
+
+The engine searches for scripts in the following order, if the regular module resolution does not find a module:
+        - /apps/esx/node_modules
+        - /apps/esx/esx_modules
+        - /libs/esx/node_modules
+        - /libs/esx/esx_modules
+
+Additionally, ESX will try to resolve the folder *esx_modules* prior to *node_modules*.
+
+### Special Loaders
+Require Extensions are deprecated (see https://nodejs.org/api/globals.html#globals_require_extensions), therefore we have not implemented/used the extension loaders api and .bin extension cannot be used.
+
+We have borrowed the requirejs loader plugin syntax instead (see http://requirejs.org/docs/api.html#text). Additionally to the standard JS loader following two loaders are existing:
+
+- text (e.g. ```require("text!./templates/header.html"))```)
+  - will return a javascript native string containing the content of the file
+- resource  (e.g. ```require("resource!./content/blogposts)```)
+  following will be exposed:
+  - properties (resource valuemap)
+  - path (jcr path)  
+  - simpleResource (has getChildren method with resolved simpleresoruce in an array)
+  - array with list of children (simpleResource)
+
+- json loader  (e.g. ```require("./dict/en.json```)
+  - the json as a whole will be exported as a javascript Object
+
+## Installing Demo Application
+Currently the demo application is bundles with the engine bundle. To install the engine with the demo application, follow this steps:
+- switch to directory src/main/resources/libs/esx/demo
+- run: npm install
+- go back to package root directory
+- run mvn clean install sling:install´
+
+open http://localhost:8080/libs/esx/demo/content/demo.html
+
+### Writing a module
+You can actually follow the NODE JS description on https://nodejs.org/api/modules.html for more detailed explanation.
+
+A module has access to following variables:
+- __filename
+- __dirname
+- console (console.log is a log4j logger registered to the resolved module path and is not a 1:1 console.log implementation for now)
+- properties (valuemap)
+- simpleResource
+- currentNode
+ - currentNode.path
+ - currentNode.resource
+ - currentNode.properties
+- sling (SlingScriptHelper)
+
+
+# Example
+## Caluclator Module
+Path: /apps/demo/components/test/helper/calculator/index.js
+```javascript
+function calculate(a, b) {
+  return a + b;
+}
+exports.math = calculate;
+```
+
+## Test components
+Path: /apps/demo/components/test/test.esx
+```javascript
+var calculator = require("./helper/calculator");
+
+exports.render = function () {
+  return calculator.math(2,2);
+}
+```
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..367ad76
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>29</version>
+        <relativePath />
+    </parent>
+
+    <groupId>org.apache.sling</groupId>
+    <artifactId>org.apache.sling.scripting.esx</artifactId>
+    <packaging>bundle</packaging>
+    <version>1.0-SNAPSHOT</version>
+    <name>Apache Sling Scripting ESX</name>
+    <description>Support NodeJS like scripting and module resolution</description>
+
+    <build>
+        <resources>
+            <!-- filter meta information to get some properties into the files -->
+            <resource>
+                <directory>${basedir}/src/main/resources</directory>
+                <targetPath>resources</targetPath>
+            </resource>
+        </resources>        
+        <plugins>           
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>animal-sniffer-maven-plugin</artifactId>
+                    <configuration>
+                        <ignores>
+                            <ignore>jdk.nashorn.*</ignore>
+                        </ignores>
+                    </configuration>
+           </plugin>
+           <plugin>
+                <groupId>org.apache.rat</groupId>
+                <artifactId>apache-rat-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>src/main/resources/libs/esx/demo/node_modules/**</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+                <version>1.21.0</version>
+                <executions>
+                    <execution>
+                        <id>generate-scr-descriptor</id>
+                        <goals>
+                            <goal>scr</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>            
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>3.0.0</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Export-Package>
+                            org.apache.sling.scripting.esx.*
+                        </Export-Package>
+                        <Private-Package></Private-Package>      
+                        <Include-Resource>{maven-resources}</Include-Resource>   
+                        <Sling-Initial-Content>
+                            resources/libs/esx;overwrite:=true;path:=/libs/esx;ignoreImportProviders:=json
+                        </Sling-Initial-Content>               
+                    </instructions>                                      
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.3</version>
+                <configuration>
+                    <source>8</source>
+                    <target>8</target>
+                    <showDeprecation>false</showDeprecation>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <version>4.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <version>4.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.event</artifactId>
+            <version>4.2.0</version>
+        </dependency>        
+        <dependency>
+            <groupId>javax.jcr</groupId>
+            <artifactId>jcr</artifactId>
+            <version>2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>1.3.2</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.0.1</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.api</artifactId>
+            <version>2.9.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.scripting.api</artifactId>
+            <version>2.1.8</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.json</artifactId>
+            <version>2.0.19-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.7.6</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+            <version>1.9.12</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.jcr.jackrabbit.server</artifactId>
+            <version>2.1.0</version>
+            <type>jar</type>
+        </dependency>
+    </dependencies>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <sling.java.version>8</sling.java.version>
+    </properties>
+    <profiles>
+        <profile>
+            <id>autoInstallBundle</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.sling</groupId>
+                        <artifactId>maven-sling-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>install-bundle</id>
+                                <goals>
+                                    <goal>install</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>                    
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/src/main/java/org/apache/sling/scripting/esx/EsxScriptEngine.java b/src/main/java/org/apache/sling/scripting/esx/EsxScriptEngine.java
new file mode 100644
index 0000000..07a8e91
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/esx/EsxScriptEngine.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.scripting.esx;
+
+import java.io.IOException;
+import java.io.Reader;
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.scripting.SlingScriptHelper;
+import org.apache.sling.scripting.api.AbstractSlingScriptEngine;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class EsxScriptEngine extends AbstractSlingScriptEngine {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    public EsxScriptEngine(EsxScriptEngineFactory scriptEngineFactory) {
+        super(scriptEngineFactory);
+    }
+
+    @Override
+    public Object eval(Reader reader, ScriptContext context) throws ScriptException {
+        log.debug("starting to eval ESX Script");
+        Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);
+        SlingScriptHelper scriptHelper = (SlingScriptHelper) bindings.get("sling");
+        Resource scriptResource = scriptHelper.getScript().getScriptResource();
+        Resource resource = scriptHelper.getRequest().getResource();
+
+        ModuleScript moduleScript = new ModuleScript(ModuleScript.JS_FILE, scriptResource);
+        //public Module (EsxScriptEngineFactory factory, Resource resource, ModuleScript moduleScript, String id, Module parent, SlingScriptHelper scriptHelper) throws ScriptException {
+
+        Module module = new Module(
+                (EsxScriptEngineFactory) getFactory(),
+                resource, moduleScript, scriptResource.getPath(), null,
+                scriptHelper,
+                Module.LOADER_JS
+        );
+        
+        try {
+            Object moduleResults = module.require(scriptResource.getPath());
+            
+            String result = ((EsxScriptEngineFactory) getFactory()).getNashornEngine().eval(
+                    "if(exports.render && typeof exports.render === 'function') { exports.render('test'); }"
+                  + " else if(typeof exports === 'class') { new exports().render('function') } else {"
+                            + "'You need to define either a render function or export an object with a render method'; }", module).toString();
+            
+            context.getWriter().write(result);
+            
+        } catch (IOException ex) {
+            throw new ScriptException(ex);
+        }        
+               
+        return null;
+    }
+}
diff --git a/src/main/java/org/apache/sling/scripting/esx/EsxScriptEngineFactory.java b/src/main/java/org/apache/sling/scripting/esx/EsxScriptEngineFactory.java
new file mode 100644
index 0000000..e588d18
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/esx/EsxScriptEngineFactory.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.scripting.esx;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.sling.scripting.api.AbstractScriptEngineFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.osgi.framework.Constants;
+import org.osgi.service.component.ComponentContext;
+
+@Component(label = "ESX Scripting Engine Factory", description = "", metatype = true)
+@Service
+@Properties({
+    @Property(name = Constants.SERVICE_VENDOR, value = "The Apache Software Foundation")
+    ,
+        @Property(name = Constants.SERVICE_DESCRIPTION, value = "Scripting Engine for Ecmas Script using Node JS like module loader")
+    ,
+        @Property(name = "compatible.javax.script.name", value = "esx")
+})
+public class EsxScriptEngineFactory extends AbstractScriptEngineFactory {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private ScriptEngine nashornScriptEngine;
+    
+    @Reference
+    ScriptModuleCache moduleCache;
+
+    public EsxScriptEngineFactory() {
+        setNames("esx", "ESX");
+        setExtensions("esx", "ESX");
+    }
+
+    @Override
+    public String getLanguageName() {
+        return "ESX";
+    }
+
+    @Override
+    public String getLanguageVersion() {
+        return "1.0";
+    }
+
+    @Override
+    public ScriptEngine getScriptEngine() {
+        return new EsxScriptEngine(this);
+    }
+
+    /**
+     * 
+     * @return 
+     */
+    public ScriptModuleCache getModuleCache() {
+        return moduleCache;        
+    }
+    
+    /**
+     *
+     * @return
+     */
+    public ScriptEngine getNashornEngine() {
+        return nashornScriptEngine;
+    }
+
+    @Deactivate
+    private void deactivate() {
+        log.debug("Deactivating Engine");
+    }
+
+    @Activate
+    protected void activate(ComponentContext context) {
+        log.debug("Starting Engine");
+        // create one script engine
+        NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
+        this.nashornScriptEngine = factory.getScriptEngine();
+        log.debug("Engine started");
+    }
+}
diff --git a/src/main/java/org/apache/sling/scripting/esx/Module.java b/src/main/java/org/apache/sling/scripting/esx/Module.java
new file mode 100644
index 0000000..d3aeb4a
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/esx/Module.java
@@ -0,0 +1,657 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.scripting.esx;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+import javax.script.ScriptException;
+import javax.script.SimpleBindings;
+import jdk.nashorn.api.scripting.JSObject;
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.scripting.SlingScriptHelper;
+import org.apache.sling.commons.json.JSONException;
+import org.apache.sling.commons.json.JSONObject;
+import org.apache.sling.scripting.esx.plugins.ConsoleLog;
+import org.apache.sling.scripting.esx.plugins.SimpleResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Module extends SimpleBindings implements Require {
+
+    public static final String CONTEXT_FIELD_SLING = "sling";
+    public static final String CONTEXT_FIELD_PARENT = "parent";
+    public static final String CONTEXT_FIELD_ID = "id";
+    public static final String CONTEXT_FIELD_MAIN = "main";
+    public static final String CONTEXT_FIELD_IS_LOADED = "isLoaded";
+    public static final String CONTEXT_FIELD_FILENAME = "filename";
+    public static final String CONTEXT_FIELD_RESOURCE = "resource";
+    public static final String CONTEXT_FIELD_CHILDREN = "children";
+    public static final String CONTEXT_FIELD_MODULE_RESOURCE = "moduleResource";
+    public static final String CONTEXT_FIELD_EXPORTS = "exports";
+    public static final String CONTEXT_FIELD_CONSOLE = "console";
+    public static final String LOADER_TEXT = "text!";
+    public static final String LOADER_RESOURCE = "resource!";
+    public static final String LOADER_JS = "js!";
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private SlingScriptHelper scriptHelper;
+    private final EsxScriptEngineFactory factory;
+    private ModuleScript moduleScript;
+    private boolean isLoaded = false;
+    private final List<Module> children = new ArrayList();
+    private String loader;
+
+    /**
+     *
+     * @param factory
+     * @param resource
+     * @param moduleScript
+     * @param id
+     * @param parent
+     * @param scriptHelper
+     * @throws ScriptException
+     */
+    public Module(EsxScriptEngineFactory factory, Resource resource,
+            ModuleScript moduleScript, String id, Module parent,
+            SlingScriptHelper scriptHelper,
+            String loader) throws ScriptException {
+        this.factory = factory;
+        this.scriptHelper = scriptHelper;
+        this.moduleScript = moduleScript;
+        this.loader = loader;
+
+        put(CONTEXT_FIELD_PARENT, parent);
+        put(CONTEXT_FIELD_SLING, scriptHelper);
+
+        put(CONTEXT_FIELD_ID, id);
+        put(CONTEXT_FIELD_MAIN, (parent == null) ? this : (Module) parent.get(CONTEXT_FIELD_PARENT));
+        put(CONTEXT_FIELD_IS_LOADED, isLoaded);
+        put(CONTEXT_FIELD_FILENAME, moduleScript.getResource().getPath());
+        put(CONTEXT_FIELD_RESOURCE, resource);
+        put(CONTEXT_FIELD_CHILDREN, children);
+        put(CONTEXT_FIELD_MODULE_RESOURCE, moduleScript.getResource());
+
+        put(CONTEXT_FIELD_EXPORTS, ((JSObject) factory.getNashornEngine().eval("Object")).newObject());
+
+        log.debug("this is the main script: " + (get(CONTEXT_FIELD_MAIN) == this));
+
+        put(CONTEXT_FIELD_CONSOLE, new ConsoleLog((String) get(CONTEXT_FIELD_FILENAME)));
+    }
+
+    /**
+     *
+     * @param source
+     * @return
+     * @throws ScriptException
+     */
+    private ScriptObjectMirror decoreateScript(String source)
+            throws ScriptException {
+
+        String polyfill = "";
+
+        if ("polyfill".equals(get(CONTEXT_FIELD_ID))) {
+            //polyfill = "require('/libs/esx/esx_modules/polyfills/index.js')(this).bind(this)";
+        }
+        source = "//@sourceURL=" + (String) get("filename") + "\n"
+                + "(function (exports, require, module, __filename,"
+                + " __dirname, currentNode, console, properties, sling, simpleResource) { "
+                + "var window = this;"
+                + "var global = this;"
+                + source
+                + "})";
+
+        // use load + filenane for older JDK versions, @sourceURL is working for latest JDK version
+        source = "load( { name : \"" + get("filename") + "\","
+                + " script: \""
+                + StringEscapeUtils.escapeEcmaScript(source)
+                + "\" } )";
+
+        ScriptObjectMirror function = null;
+        try {
+            function = (ScriptObjectMirror) factory.getNashornEngine().eval(
+                    source
+            );
+            if (function == null) {
+                log.error("Function is null !");
+            }
+        } catch (ScriptException ex) {
+            // todo: better handling in future
+            throw ex;
+        }
+        return function;
+    }
+
+    /**
+     *
+     * @return @throws ScriptException
+     */
+    public Object runScript() throws ScriptException {
+        log.debug("run script with id {}", get("id"));
+        ScriptObjectMirror function = factory.getModuleCache().get((String) get(CONTEXT_FIELD_FILENAME));
+
+        Resource moduleResource = (Resource) get(CONTEXT_FIELD_MODULE_RESOURCE);
+        Resource resource = (Resource) get(CONTEXT_FIELD_RESOURCE);
+
+        if (function == null) {
+            if (moduleScript.isJsFile()) {
+                function = decoreateScript(
+                        //readScript(moduleResource)//  
+                        readScript(moduleResource)
+                );
+            }
+
+            if (moduleScript.isJsonFile()) {
+                String jsonfile = readScript(moduleResource);
+                function = decoreateScript(
+                        "module.exports = " + jsonfile
+                );
+            }
+
+            if (moduleScript.isResourceFile()) {
+                Iterator<Resource> resChildren = moduleResource.listChildren();
+                ArrayList<Resource> children = new ArrayList<Resource>();
+                resChildren.forEachRemaining(children::add);
+
+                put("children", children);
+
+                ValueMap map = moduleResource.adaptTo(ValueMap.class);
+
+                JSONObject values = new JSONObject(map);
+
+                String jsonprop = values.toString();
+
+                SimpleResource simpleResource = moduleResource.adaptTo(SimpleResource.class);
+                put("simpleResource", simpleResource);
+
+                String source = "exports.properties =  " + jsonprop + ";"
+                        + "exports.path = currentNode.resource.path;"
+                        + "exports.simpleResource = this.simpleResource;"
+                        + "exports.children = this.children;";
+
+                function = decoreateScript(source);
+            }
+            if (!moduleScript.isResourceFile()) {
+                factory.getModuleCache().put(moduleScript.getResource().getPath(), function);
+            }
+        } else {
+            log.debug("module " + get(CONTEXT_FIELD_ID) + " received from cache");
+        }
+
+        if (moduleScript.isTextFile()) {
+            log.debug("is textfile loaidng file");
+            String source = StringEscapeUtils.escapeEcmaScript(readScript(moduleResource));
+            log.debug("sourcE: ");
+            source = "module.exports = \"" + source + "\";";
+            log.debug(source);
+            function = decoreateScript(source);
+            factory.getModuleCache().put(moduleScript.getResource().getPath(), function);
+        }
+        JSObject process = (JSObject) factory.getNashornEngine().eval("Object");
+        process.setMember("domain", log);
+
+        if (function != null) {
+            SimpleBindings currentNode = new SimpleBindings();
+            if (resource != null) {
+                currentNode.put("resource", resource);
+                currentNode.put("properties", resource.adaptTo(ValueMap.class));
+            } else {
+                log.debug("module id {} resource is null", get(CONTEXT_FIELD_ID));
+            }
+
+            function.call(this, get(CONTEXT_FIELD_EXPORTS), (Require) this::require, this, get(CONTEXT_FIELD_FILENAME),
+                    ((Resource) get(CONTEXT_FIELD_MODULE_RESOURCE)).getParent().getPath(), currentNode,
+                    (ConsoleLog) get(CONTEXT_FIELD_CONSOLE),
+                    null,
+                    (SlingScriptHelper) get(CONTEXT_FIELD_SLING),
+                    resource.adaptTo(SimpleResource.class)
+            );
+
+        } else {
+            log.warn("function not called because it is null");
+        }
+
+        put(CONTEXT_FIELD_EXPORTS, get(CONTEXT_FIELD_EXPORTS));
+
+        return get(CONTEXT_FIELD_EXPORTS);
+    }
+
+    /**
+     *
+     * @param script
+     * @return
+     */
+    public String readScript(Resource script) throws ScriptException {
+        InputStream is = script.getChild("jcr:content").adaptTo(InputStream.class);
+        BufferedReader esxScript = new BufferedReader(new InputStreamReader(is));
+        StringBuilder buffer = new StringBuilder();
+        String temp;
+        try {
+            while ((temp = esxScript.readLine()) != null) {
+                buffer.append(temp).append("\r\n");
+            }
+            return buffer.toString();
+        } catch (IOException ioex) {
+            throw new ScriptException(ioex);
+        }
+    }
+
+    /**
+     *
+     * @param path
+     * @return
+     */
+    private boolean isLocalModule(String path) {
+        return (path.startsWith("./") == true
+                || path.startsWith("/") == true
+                || path.startsWith("../") == true);
+    }
+
+    /**
+     *
+     * @param path
+     * @param basePath
+     * @return
+     */
+    private String normalizePath(String path, String basePath) {
+        path = StringUtils.removeStart(cleanModulePathFromLoaders(path), basePath);
+        return ResourceUtil.normalize(basePath + "/" + path);
+    }
+
+    /**
+     * not implemented yet
+     *
+     * @param path
+     * @return
+     */
+    private boolean isGlobalModule(String path) {
+        return false;
+    }
+
+    /**
+     *
+     * @param file
+     * @param type
+     * @return
+     * @throws ScriptException
+     */
+    private ModuleScript createModuleScript(Resource file, int type) throws ScriptException {
+        log.debug("module created. " + file.getPath());
+        Node currentNode = file.adaptTo(Node.class);
+        if (currentNode != null) {
+            log.debug("currentNode !) null = " + (currentNode != null));
+            try {
+                boolean isFile = currentNode.isNodeType(NodeType.NT_FILE);
+                log.debug("isFile: " + isFile);
+                if (isFile) {
+                    return new ModuleScript(type, file);
+                }
+                log.debug("not a file " + currentNode.getMixinNodeTypes().toString());
+            } catch (RepositoryException ex) {
+                throw new ScriptException("cannot load file " + file.getPath());
+            }
+        }
+        return null;
+
+    }
+
+    /**
+     *
+     * @param module
+     * @param path
+     * @param currentResource
+     * @return
+     * @throws ScriptException
+     */
+    public ModuleScript loadAsFile(String module, String path,
+            Resource currentResource, String loader) throws ScriptException {
+        int type = ModuleScript.JS_FILE;
+
+        // this is need to be refactored, it is this way because I followed the
+        // node.js extension handling at first but switched over to requirejs
+        // loader notation        
+        Resource file = currentResource.getResourceResolver().getResource(path);
+
+        // require.extensions is deprecated, however to implement this might 
+        // be a good way to handle .resource loading or to implemend loader 
+        // like in requirejs e.g. similar to https://github.com/requirejs/text
+        // "text!some/module.html" 
+        // or require("resource!/content/homepage/jcr:content")
+        if (LOADER_RESOURCE.equals(loader) && file != null) {
+            return new ModuleScript(ModuleScript.RESOURCE_FILE, file);
+        }
+
+        if (LOADER_TEXT.equals(loader) && file != null) {
+            return new ModuleScript(ModuleScript.TEXT_FILE, file);
+        }
+
+        //special handling for json file require
+        if (path.endsWith(".json") && file != null) {
+            return new ModuleScript(ModuleScript.JSON_FILE, file);
+        }
+
+        if (path.endsWith(".bin") && file != null) {
+            log.warn(".bin loder are currently not supported (file  requested: " + path);
+        }
+
+        try {
+            if (file == null || !file.adaptTo(Node.class).isNodeType(NodeType.NT_FILE)) {
+                file = currentResource.getResourceResolver().getResource(path + ".js");
+                if (file == null) {
+                    file = currentResource.getResourceResolver().getResource(path + ".json");
+                    if (file == null) {
+                        return null;
+                    }
+                    type = ModuleScript.JSON_FILE;
+                } else {
+                    type = ModuleScript.JS_FILE;
+                }
+            }
+        } catch (RepositoryException ex) {
+            log.error(module + "", ex);
+        }
+
+        return createModuleScript(file, type);
+    }
+
+    private String cleanModulePathFromLoaders(String path) {
+        if (path.startsWith("resource!")) {
+            return path.substring("resource!".length(), path.length());
+        }
+
+        if (path.startsWith("text!")) {
+            return path.substring("text!".length(), path.length());
+        }
+
+        return path;
+    }
+
+    /**
+     *
+     * @param module
+     * @param path
+     * @param currentResource
+     * @return
+     * @throws ScriptException
+     */
+    public ModuleScript loadAsDirectory(String module, String path, Resource currentResource, String loader) throws ScriptException {
+
+        ResourceResolver resolver = currentResource.getResourceResolver();
+        Resource packageJson = resolver.getResource(path + "/package.json");
+
+        if (packageJson != null) {
+            Node jsonFile = packageJson.adaptTo(Node.class);
+
+            try {
+                boolean isFile = (jsonFile.isNodeType(NodeType.NT_FILE) || jsonFile.isNodeType(NodeType.NT_RESOURCE));
+
+                if (isFile) {
+
+                    InputStream is = packageJson.getChild("jcr:content").adaptTo(InputStream.class);
+                    try {
+                        String jsonData = IOUtils.toString(is);
+
+                        JSONObject json;
+                        try {
+                            json = new JSONObject(jsonData);
+                        } catch (JSONException ex) {
+                            throw new ScriptException(ex);
+                        }
+
+                        if (json.has("main")) {
+                            String packageModule;
+                            try {
+                                packageModule = json.getString("main");
+                            } catch (JSONException ex) {
+                                throw new ScriptException(ex);
+                            }
+
+                            String mainpath = normalizePath(packageModule,
+                                    path);
+
+                            return loadAsFile(packageModule, mainpath, currentResource, loader);
+                        }
+
+                    } catch (IOException ex) {
+                        throw new ScriptException(ex);
+                    }
+                }
+
+            } catch (RepositoryException ex) {
+                throw new ScriptException(ex);
+            }
+
+        }
+
+        Resource indexjs = resolver.getResource(path + "/index.js");
+
+        if (indexjs != null) {
+            return createModuleScript(indexjs, ModuleScript.JS_FILE);
+        }
+
+        Resource indexjson = resolver.getResource(path + "/index.json");
+
+        if (indexjson != null) {
+            return createModuleScript(indexjson, ModuleScript.JSON_FILE);
+        }
+
+        Resource indexnode = resolver.getResource(path + "/index.node");
+        if (indexnode != null) {
+            throw new ScriptException("Node module .node (binary) loading is currently not supported");
+        }
+
+        return null;
+    }
+
+    /**
+     *
+     * @param module
+     * @param currentResource
+     * @return
+     * @throws ScriptException
+     */
+    public ModuleScript loadAsModule(String module, Resource currentResource, String loader) throws ScriptException {
+        return loadAsModule(module, currentResource, true, loader);
+    }
+
+    /**
+     *
+     * @param paths
+     * @return
+     */
+    private String[] loadModulePaths(String paths) {
+        String[] parts = paths.split("/");
+        List<String> dirs = new ArrayList<String>();
+
+        for (int i = (parts.length - 1); i > 0;) {
+            log.debug(parts[i]);
+            if (parts[i] == "node_modules" || parts[i] == "esx_modules") {
+                continue;
+            }
+            String part = StringUtils.join(parts, "/", 0, i);
+            String dir = part + "/node_modules";
+            log.debug("load dir: " + dir);
+            dirs.add(part + "/esx_modules");
+            dirs.add(dir);
+            i = i - 1;
+        }
+
+        // if the regular module resoultion is not finding anything, try and check the
+        // global paths. needs to be optimized.
+        dirs.add("/apps/esx/esx_modules");
+        dirs.add("/apps/esx/node_modules");
+        dirs.add("/libs/esx/esx_modules");
+        dirs.add("/libs/esx/node_modules");
+
+        return dirs.stream().toArray(String[]::new);
+    }
+
+    /**
+     *
+     * @param module
+     * @param currentResource
+     * @param isFileResource
+     * @return
+     * @throws ScriptException
+     */
+    public ModuleScript loadAsModule(String module, Resource currentResource, boolean isFileResource, String loader) throws ScriptException {
+        ModuleScript script = null;
+
+        String[] dirs = loadModulePaths(currentResource.getPath());
+        log.debug("loading modules from dir path");
+        for (String dir : dirs) {
+            log.debug("trying to resolve. " + dir);
+            Resource searchPath = currentResource.getResourceResolver().resolve(dir);
+            log.debug("searchpath  = " + searchPath.getPath());
+            if (searchPath != null && !ResourceUtil.isNonExistingResource(searchPath)) {
+                log.debug("searchpath loadasmodule: " + searchPath.getPath());
+                script = loadLocalModule(module, searchPath, false, loader);
+                if (script != null) {
+                    return script;
+                }
+            } else {
+                log.debug("dir is null = " + dir);
+            }
+        }
+        log.debug("finsihed loading dirs, script is loaded?!");
+        return script;
+    }
+
+    /**
+     *
+     * @param module
+     * @param currentResource
+     * @return
+     * @throws ScriptException
+     */
+    public ModuleScript loadLocalModule(String module, Resource currentResource, String loader) throws ScriptException {
+        return loadLocalModule(module, currentResource, false, loader);
+    }
+
+    /**
+     *
+     * @param resource
+     * @return
+     */
+    private boolean resourceIsfile(Resource resource) {
+        try {
+            return resource.adaptTo(Node.class).isNodeType(NodeType.NT_FILE);
+        } catch (RepositoryException ex) {
+            log.error("resourceIsfile", ex);
+        }
+        return false;
+    }
+
+    /**
+     *
+     * @param module
+     * @param currentResource
+     * @param isFile
+     * @return
+     * @throws ScriptException
+     */
+    public ModuleScript loadLocalModule(String module, Resource currentResource, boolean isFile, String loader) throws ScriptException {
+        String basePath = (resourceIsfile(currentResource))
+                ? currentResource.getParent().getPath() : currentResource.getPath();
+        String path = normalizePath(module, basePath);
+
+        if (module.startsWith("/")) {
+            path = module;
+        }
+        ModuleScript script = loadAsFile(module, path, currentResource, loader);
+
+        if (script != null) {
+            return script;
+        }
+
+        return loadAsDirectory(module, path, currentResource, loader); // load as directory                              
+    }
+
+    /**
+     * p´
+     *
+     * @param module
+     * @param currentResource
+     * @return
+     * @throws javax.script.ScriptException
+     */
+    public ModuleScript resolve(String module, Resource currentResource, String loader) throws ScriptException {
+        // if x is core module / library return directly the one
+        log.debug("resolving module: " + module);
+        ModuleScript script;
+
+        if (isGlobalModule(module)) {
+            // ignore for now
+        }
+
+        if (isLocalModule(module)) {
+            script = loadLocalModule(module, currentResource, loader);
+            if (script != null) {
+                return script;
+            }
+        }
+
+        // load as module (first split path, then load)
+        script = loadAsModule(module, currentResource, loader);
+        if (script != null) {
+            return script;
+        }
+
+        throw new ScriptException("module not found " + module);
+    }
+
+    @Override
+    public Object require(String id) throws ScriptException, IOException {
+        log.debug("Trying to require Module with Id: " + id);
+        // different behavior if we are the main module, require directly
+        // run runScript directly on this module
+        if (get("id").equals(id)
+                && get("main") == this) {
+            return runScript();
+        }
+
+        String loader = LOADER_JS;
+        if (id.startsWith(LOADER_TEXT)) {
+            loader = LOADER_TEXT;
+        }
+
+        if (id.startsWith(LOADER_RESOURCE)) {
+            loader = LOADER_RESOURCE;
+        }
+        ModuleScript subModuleScript = resolve(cleanModulePathFromLoaders(id), (Resource) get(CONTEXT_FIELD_MODULE_RESOURCE), loader);
+
+        Module subModule = new Module(factory, (Resource) get(CONTEXT_FIELD_RESOURCE),
+                subModuleScript, id, this, (SlingScriptHelper) get(CONTEXT_FIELD_SLING), loader);
+        Object result = subModule.runScript();
+        children.add(subModule);
+        subModule.put(CONTEXT_FIELD_IS_LOADED, true);
+        return result;
+    }
+}
diff --git a/src/main/java/org/apache/sling/scripting/esx/ModuleScript.java b/src/main/java/org/apache/sling/scripting/esx/ModuleScript.java
new file mode 100644
index 0000000..9ae80da
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/esx/ModuleScript.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.scripting.esx;
+
+import org.apache.sling.api.resource.Resource;
+
+public class ModuleScript {
+    
+    /**
+     * 
+     */
+    public static int JS_FILE = 1;
+    
+    /**
+     * 
+     */
+    public static int JSON_FILE = 2;
+    
+    /**
+     * 
+     */
+    public static int RESOURCE_FILE = 3;
+    
+    /**
+     * 
+     */
+    public static int TEXT_FILE = 4;
+    
+    
+    private int type;
+    private Resource resource;
+    
+    /**
+     * 
+     * @param type
+     * @param resource 
+     */
+    public ModuleScript(int type, Resource resource) {
+        this.type = type;
+        this.resource = resource;
+    }
+    
+    /**
+     * 
+     * @return 
+     */
+    public boolean isJsFile() {
+        return (type == JS_FILE);
+    }
+
+    /**
+     * 
+     * @return 
+     */
+    public boolean isJsonFile() {
+        return (type == JSON_FILE);
+    }
+    
+    /**
+     * 
+     * @return 
+     */
+    public boolean isResourceFile() {
+        return (type == RESOURCE_FILE);
+    }
+    
+    /**
+     * 
+     * @return 
+     */
+    public boolean isTextFile() {
+        return (type == TEXT_FILE);
+    }
+    
+    /**
+     * 
+     * @return 
+     */
+    public Resource getResource() {
+        return resource;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/scripting/esx/Require.java b/src/main/java/org/apache/sling/scripting/esx/Require.java
new file mode 100644
index 0000000..7d13380
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/esx/Require.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.scripting.esx;
+
+import java.io.IOException;
+import javax.script.ScriptException;
+
+/**
+ *
+ * @author stas
+ */
+@FunctionalInterface
+public interface Require {
+    public Object require(String id) throws ScriptException, IOException;
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/scripting/esx/ScriptModuleCache.java b/src/main/java/org/apache/sling/scripting/esx/ScriptModuleCache.java
new file mode 100644
index 0000000..d1f4f02
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/esx/ScriptModuleCache.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.scripting.esx;
+
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import org.apache.sling.api.resource.Resource;
+import org.osgi.service.event.EventHandler;
+
+public interface ScriptModuleCache extends EventHandler{
+
+    /**
+     * put compiled script into script module cache
+     * @param path
+     * @param script
+     */
+    public void put(String module, ScriptObjectMirror script);
+
+    /**
+     * get compiled script or null if the script is not in the cache
+     * @param path
+     * @return
+     */
+    public ScriptObjectMirror get(String module);
+
+    /**
+     * 
+     * @param resource
+     * @return 
+     */
+    public ScriptObjectMirror get(Resource resource);
+    
+
+    /**
+     * removing module script from cache if existing otherwise
+     * doesn't do anything (return always true)
+     *
+     * return false if for some reason cannot be flushed from cache
+     *
+     * @param path
+     */
+    public boolean flush(String module);    
+    
+}
diff --git a/src/main/java/org/apache/sling/scripting/esx/plugins/ConsoleLog.java b/src/main/java/org/apache/sling/scripting/esx/plugins/ConsoleLog.java
new file mode 100644
index 0000000..00e2615
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/esx/plugins/ConsoleLog.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.scripting.esx.plugins;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConsoleLog {
+
+    Logger log;
+
+    public ConsoleLog(String scriptId) {
+        this.log = LoggerFactory.getLogger(scriptId);
+    }
+
+    public void log(String msg) {
+        log.info(msg);
+    }
+
+    public void info(String msg) {
+        log.info(msg);
+    }
+
+    public void warn(String msg) {
+        log.warn(msg);
+    }
+
+    public void error(String msg) {
+        log.error(msg);
+    }
+
+    public Logger getLogger() {
+        return log;
+    }
+}
diff --git a/src/main/java/org/apache/sling/scripting/esx/plugins/SimpleResource.java b/src/main/java/org/apache/sling/scripting/esx/plugins/SimpleResource.java
new file mode 100644
index 0000000..9843ef9
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/esx/plugins/SimpleResource.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.scripting.esx.plugins;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
+
+public class SimpleResource {
+
+    private final Resource resource;
+    private final ValueMap valueMap;
+
+    public SimpleResource(Resource resource) {
+        this.resource = resource;
+        this.valueMap = resource.getValueMap();
+    }
+
+    public String getPath() {
+        return resource.getPath();
+    }
+
+    public String getResourceType() {
+        return resource.getResourceType();
+    }
+
+    public String getResourceSuperType() {
+        return resource.getResourceSuperType();
+    }
+
+    public SimpleResource getParent() {
+        return resource.getParent().adaptTo(SimpleResource.class);
+    }
+
+    public boolean hasChildren() {
+        return resource.hasChildren();
+    }
+
+    public boolean isResourceType(String resourceType) {
+        return resource.isResourceType(resourceType);
+    }
+
+    public ValueMap getValueMap() {
+        return valueMap;
+    }
+    
+    public ValueMap getProperties() {
+        return getValueMap();
+    }
+    
+    public String getStringProperty(String key) {
+        return valueMap.get(key, String.class);
+    }
+    
+    public long getDateTimeProperty(String key) {
+        return ((Calendar) valueMap.get(key, Calendar.class)).getTime().getTime();
+    }
+    
+    public Object[] getArray(String key) {
+        return (Object[]) valueMap.get(key, Object[].class);
+    }
+
+    public SimpleResource getChild(String childName) {
+        return resource.getChild(childName).adaptTo(SimpleResource.class);
+    }
+    
+    public ResourceResolver getResourceResolver() {
+        return resource.getResourceResolver();
+    }
+    public List<SimpleResource> getChildren() {
+        Iterator<Resource> resChildren = resource.listChildren();
+        ArrayList<SimpleResource> children = new ArrayList<SimpleResource>();
+        resChildren.forEachRemaining(resource -> children.add(resource.adaptTo(SimpleResource.class)));
+        return children;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/scripting/esx/plugins/SimpleResourceAdapterFactory.java b/src/main/java/org/apache/sling/scripting/esx/plugins/SimpleResourceAdapterFactory.java
new file mode 100644
index 0000000..3e4daec
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/esx/plugins/SimpleResourceAdapterFactory.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.scripting.esx.plugins;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.adapter.AdapterFactory;
+import org.apache.sling.api.resource.Resource;
+
+@Component
+@Service
+public class SimpleResourceAdapterFactory implements AdapterFactory {
+
+    @Property(name = "adapters")
+    public static final String[] ADAPTER_CLASSES = {
+        SimpleResource.class.getName()
+    };
+    @Property(name = "adaptables")
+    public static final String[] ADAPTABLE_CLASSES = {
+        Resource.class.getName()
+    };
+
+    @Override
+    public <AdapterType> AdapterType getAdapter(Object adaptableObject, Class<AdapterType> type) {
+        if (type.equals(SimpleResource.class)
+                && adaptableObject instanceof Resource) {
+
+            SimpleResource simpeResource = new SimpleResource((Resource) adaptableObject);
+
+            return (AdapterType) simpeResource;
+        } else {
+            return null;
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/scripting/esx/services/ScriptModuleCache.java b/src/main/java/org/apache/sling/scripting/esx/services/ScriptModuleCache.java
new file mode 100644
index 0000000..5f92100
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/esx/services/ScriptModuleCache.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.scripting.esx.services;
+
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import org.apache.sling.api.resource.Resource;
+import org.osgi.service.event.EventHandler;
+
+public interface ScriptModuleCache extends EventHandler{
+
+    /**
+     * put compiled script into script module cache
+     * @param path
+     * @param script
+     */
+    public void put(String module, ScriptObjectMirror script);
+
+    /**
+     * get compiled script or null if the script is not in the cache
+     * @param path
+     * @return
+     */
+    public ScriptObjectMirror get(String module);   
+
+    /**
+     * 
+     * @param resource
+     * @return 
+     */
+    public ScriptObjectMirror get(Resource resource);
+   
+
+    /**
+     * removing module script from cache if existing otherwise
+     * doesn't do anything (return always true)
+     *
+     * return false if for some reason cannot be flushed from cache
+     *
+     * @param path
+     */
+    public boolean flush(String module);        
+    
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/scripting/esx/services/impl/RepositoryModuleCache.java b/src/main/java/org/apache/sling/scripting/esx/services/impl/RepositoryModuleCache.java
new file mode 100644
index 0000000..d62182e
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/esx/services/impl/RepositoryModuleCache.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.scripting.esx.services.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.SlingConstants;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.scripting.esx.ScriptModuleCache;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component
+@Service(value = {EventHandler.class, ScriptModuleCache.class})
+@Property(name = org.osgi.service.event.EventConstants.EVENT_TOPIC,
+        value = {SlingConstants.TOPIC_RESOURCE_CHANGED, SlingConstants.TOPIC_RESOURCE_REMOVED})
+public class RepositoryModuleCache implements ScriptModuleCache {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private Map<String, ScriptObjectMirror> cache = new HashMap<String, ScriptObjectMirror>();
+
+    @Override
+    public void put(String module, ScriptObjectMirror script) {
+        log.debug("putting module with absolute path {} in the cache", module);
+        cache.put(module, script);
+    }
+
+    @Override
+    public ScriptObjectMirror get(String module) {
+        return cache.get(module);
+    }
+
+    @Override
+    public ScriptObjectMirror get(Resource resource) {
+        return cache.get(resource.getPath());
+    }
+
+    @Override
+    public boolean flush(String module) {
+        Object res = cache.remove(module);
+        if (res != null) {
+            log.debug("{} flushed from cache", module);
+        }
+        return (true);
+    }
+
+    @Override
+    public void handleEvent(Event event) {
+        final String eventPath = (String) event.getProperty(SlingConstants.PROPERTY_PATH);
+        if (cache.remove(eventPath) != null) {
+            log.info("{} was removed from cache", eventPath);
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/src/main/resources/libs/esx/demo/README.md b/src/main/resources/libs/esx/demo/README.md
new file mode 100644
index 0000000..2f7c8c5
--- /dev/null
+++ b/src/main/resources/libs/esx/demo/README.md
@@ -0,0 +1,42 @@
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+# Apache Sling ESX Quick Demo
+
+This is a simple application demonstrating some of the ESX script engine possibilities and ideas how to use it.
+
+In future we want to switch to full ES6 syntax.
+
+
+The demo purpose of this demonstration is to show the usage of npm libraries.
+
+following NPM modules are demonstrated:
+- "handlebars": "^4.0.6",
+- "highlight.js": "^9.10.0",
+- "marked": "^0.3.6",
+- "moment": "^2.18.0",
+- "underscore": "^1.8.3"
+
+## Installation
+
+- switch to directory src/main/resources/libs/esx/demo
+- run: npm install
+- go back to package root directory
+- run mvn clean install sling:install´
+
+open the page /libs/esx/demo/content/demo.html
diff --git a/src/main/resources/libs/esx/demo/components/esx_modules/BlogComponent/index.js b/src/main/resources/libs/esx/demo/components/esx_modules/BlogComponent/index.js
new file mode 100644
index 0000000..e66018f
--- /dev/null
+++ b/src/main/resources/libs/esx/demo/components/esx_modules/BlogComponent/index.js
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var Handlebars = require("handlebars");
+var marked = require("marked");
+var URL = require("url");
+
+var renderer = new marked.Renderer();
+renderer.blockquote = function (quote) {
+  return "<blockquote class='blockquote'>" + quote + "</blockquote>";
+}
+
+function resolveTitle(res) {
+   return  res.properties.navTitle || res.properties.title || res.properties["jcr:title"] || res.name;
+
+}
+
+function BlogComponent () {
+  this.templateURL = "../../hbstemplates/layout.html"
+  this.basePath = URL.getAbsoluteParent(currentNode.resource.path, 4);
+  this.baseResource = require("resource!" + this.basePath);
+  this.stylesheet = "";
+  this.partialContentTemplateURL = "../../hbstemplates/content.html";
+  this.model = currentNode;
+  this.model.title = currentNode.properties.pageTitle || currentNode.properties.title || currentNode.properties["jcr:title"] || currentNode.resource.name;
+}
+
+BlogComponent.prototype.transformMarkdown = function (content) {
+  return marked(content, { renderer: renderer});
+}
+BlogComponent.prototype.pages = function () {
+  var pages = [];
+  var homePage =
+    pages.push({
+        path: this.basePath,
+        title: resolveTitle(this.baseResource),
+        active: (currentNode.resource.path === this.basePath ? 'active' : '')
+    });
+
+  var rootPage = require("resource!" + this.basePath + "/pages");
+   var children = rootPage.simpleResource.children;
+
+    for(var key in children) {
+        var child =  children[key];
+        var nav = {};
+        nav.path =child.path;
+        nav.title =  resolveTitle(child) ;
+        nav.active = (currentNode.resource.path === child.path ? 'active' : '');
+        pages.push(nav);
+    }
+
+  return pages;
+}
+
+BlogComponent.prototype.render = function () {
+  this.init();
+  Handlebars.registerPartial('content',require("text!" + this.partialContentTemplateURL));
+
+
+  var templateSource= require("text!" + this.templateURL);
+  var template = Handlebars.compile(templateSource);
+  this.model.style = this.stylesheet;
+  this.model.pagesNav = this.pages();
+
+  return template(this.model);
+}
+
+module.exports = BlogComponent;
diff --git a/src/main/resources/libs/esx/demo/components/esx_modules/calculator/index.js b/src/main/resources/libs/esx/demo/components/esx_modules/calculator/index.js
new file mode 100644
index 0000000..1de4ffa
--- /dev/null
+++ b/src/main/resources/libs/esx/demo/components/esx_modules/calculator/index.js
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+function calulate(a, b) {
+  return a + b;
+}
+exports.math = calulate;
+exports.VERSION = "version 1.1 " + __dirname;
diff --git a/src/main/resources/libs/esx/demo/components/hbstemplates/content.html b/src/main/resources/libs/esx/demo/components/hbstemplates/content.html
new file mode 100644
index 0000000..2068e20
--- /dev/null
+++ b/src/main/resources/libs/esx/demo/components/hbstemplates/content.html
@@ -0,0 +1,17 @@
+{{!--/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */--}}
+content partial template, please overwrite
\ No newline at end of file
diff --git a/src/main/resources/libs/esx/demo/components/hbstemplates/layout.html b/src/main/resources/libs/esx/demo/components/hbstemplates/layout.html
new file mode 100644
index 0000000..83fdd98
--- /dev/null
+++ b/src/main/resources/libs/esx/demo/components/hbstemplates/layout.html
@@ -0,0 +1,68 @@
+{{!--/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */--}}<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <!-- Required meta tags -->
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <!-- Bootstrap CSS -->
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
+    <style>
+    {{{style}}}
+    </style>
+    <title>{{title}}</title>
+  </head>
+  <body>
+    <nav  class="navbar navbar-light bg-faded navbar-toggleable-md" style="background-color:#f9bb00">
+      <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
+   <span class="navbar-toggler-icon"></span>
+ </button>
+      <a class="navbar-brand" href="#">
+        <img src="https://sling.apache.org/res/logo.png" height="50">
+      </a>
+
+
+      <div class="collapse navbar-collapse" id="navbarNav">
+        <ul class="navbar-nav container">
+          {{#each pagesNav}}
+           <li class="nav-item {{active}}">
+              <a class="nav-link" href="{{path}}.html">{{title}}</a>
+          </li>
+          {{/each}}
+        </ul>
+      </div>
+
+    </nav>
+
+<div class="jumbotron jumbotron-fluid">
+  <div class="container">   <p class="lead">{{date}}</p>
+    <h1 class="display-2">{{title}}</h1>
+    <p class="lead">{{description}}</p>
+  </div>
+</div>
+  <div class="container">
+    {{> content }}
+  </div>
+
+
+
+    <!-- jQuery first, then Tether, then Bootstrap JS. -->
+    <script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" crossorigin="anonymous"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" crossorigin="anonymous"></script>
+    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous"></script>
+  </body>
+</html>
diff --git a/src/main/resources/libs/esx/demo/components/page/helper/calculator/index.js b/src/main/resources/libs/esx/demo/components/page/helper/calculator/index.js
new file mode 100644
index 0000000..6670dc5
--- /dev/null
+++ b/src/main/resources/libs/esx/demo/components/page/helper/calculator/index.js
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+function calulate(a, b) {
+  return a + b;
+}
+exports.math = calulate;
+exports.VERSION = "version 1.1";
\ No newline at end of file
diff --git a/src/main/resources/libs/esx/demo/components/page/helper/pathinfo/index.js b/src/main/resources/libs/esx/demo/components/page/helper/pathinfo/index.js
new file mode 100644
index 0000000..fba66fa
--- /dev/null
+++ b/src/main/resources/libs/esx/demo/components/page/helper/pathinfo/index.js
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var _ = require('underscore');
+var pathInfo = sling.request.requestPathInfo;
+
+/**
+ *
+ * @param {string} selector to be searched for
+ * @return {boolean}
+ */
+function hasSelector(selector) {
+    return _.contains(pathInfo.selectors, selector);
+}
+
+exports.extension = pathInfo.extension;
+exports.hasSelector = hasSelector;
\ No newline at end of file
diff --git a/src/main/resources/libs/esx/demo/components/page/page.esx b/src/main/resources/libs/esx/demo/components/page/page.esx
new file mode 100644
index 0000000..630001a
--- /dev/null
+++ b/src/main/resources/libs/esx/demo/components/page/page.esx
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var math = require("calculator");
+var pathInfo = require("./helper/pathinfo");
+var moment = require("moment");
+var _ = require('underscore');
+
+var BlogComponent = require("BlogComponent");
+
+function PageComponent() {
+    BlogComponent.call(this);
+    this.partialContentTemplateURL = __dirname + "/templates/listing.html"
+}
+PageComponent.prototype = Object.create(BlogComponent.prototype);
+
+PageComponent.prototype.init = function () {
+    var list = [];
+    var posts = require("resource!" + this.basePath + "/posts").simpleResource.children;
+    for each(var post in posts) {
+        var blogPostModel = {};
+        var createdAt = parseInt(post.getDateTimeProperty("jcr:created"));
+        blogPostModel.content = this.transformMarkdown(post.properties.content);
+        blogPostModel.properties = post.properties;
+        blogPostModel.path = post.path;
+        blogPostModel.date = moment(createdAt).format('MMMM Do YYYY, h:mm:ss a');
+        list.push(blogPostModel);
+    }
+
+    this.model.posts = list;
+}
+
+module.exports = new PageComponent();
\ No newline at end of file
diff --git a/src/main/resources/libs/esx/demo/components/page/templates/listing.html b/src/main/resources/libs/esx/demo/components/page/templates/listing.html
new file mode 100644
index 0000000..01eef36
--- /dev/null
+++ b/src/main/resources/libs/esx/demo/components/page/templates/listing.html
@@ -0,0 +1,29 @@
+{{!--/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */--}}
+{{#each posts}}
+<div class="row">
+<div class="card">
+<div class="card-block">
+    <h4 class="card-title">{{this.properties.jcr:title}}</h4>
+    <h6 class="card-subtitle mb-2 text-muted">{{date}}</h6>
+    <p class="card-text">{{{this.content}}}</p>
+    <a href="{{this.path}}.html" class="card-link">more</a>
+</div>
+</div>
+</div>
+<div class="row">&nbsp;</div>
+{{/each}}
diff --git a/src/main/resources/libs/esx/demo/components/page/templates/mobile.html b/src/main/resources/libs/esx/demo/components/page/templates/mobile.html
new file mode 100644
index 0000000..c2f1f77
--- /dev/null
+++ b/src/main/resources/libs/esx/demo/components/page/templates/mobile.html
@@ -0,0 +1,17 @@
+{{!--/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */--}}
+ mobile selector
diff --git a/src/main/resources/libs/esx/demo/components/post/post.esx b/src/main/resources/libs/esx/demo/components/post/post.esx
new file mode 100644
index 0000000..e88a995
--- /dev/null
+++ b/src/main/resources/libs/esx/demo/components/post/post.esx
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var moment = require("moment");
+var _ = require('underscore');
+var BlogComponent = require("BlogComponent");
+
+function BlogPostComponent() {
+    BlogComponent.call(this);
+    this.partialContentTemplateURL = __dirname + "/templates/detail.html"
+}
+BlogPostComponent.prototype = Object.create(BlogComponent.prototype);
+
+BlogPostComponent.prototype.init = function () {
+    var createdAt = parseInt(simpleResource.getDateTimeProperty("jcr:created"));
+    this.model.blogpost = this.transformMarkdown(currentNode.properties.content);
+    this.model.date = moment(createdAt).format('MMMM Do YYYY');
+}
+
+module.exports = new BlogPostComponent();
\ No newline at end of file
diff --git a/src/main/resources/libs/esx/demo/components/post/templates/detail.html b/src/main/resources/libs/esx/demo/components/post/templates/detail.html
new file mode 100644
index 0000000..c5befa2
--- /dev/null
+++ b/src/main/resources/libs/esx/demo/components/post/templates/detail.html
@@ -0,0 +1,17 @@
+{{!--/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */--}}
+{{{blogpost}}}
diff --git a/src/main/resources/libs/esx/demo/content/demo.xml b/src/main/resources/libs/esx/demo/content/demo.xml
new file mode 100644
index 0000000..18ff3ca
--- /dev/null
+++ b/src/main/resources/libs/esx/demo/content/demo.xml
@@ -0,0 +1,175 @@
+<!--
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<node>
+  <primaryNodeType>sling:Folder</primaryNodeType>
+  <property>
+      <name>sling:resourceType</name>
+      <value>esx/demo/components/page</value>
+  </property>
+  <property>
+      <name>jcr:title</name>
+      <value>ESX Demo</value>
+  </property>
+  <property>
+    <name>pageTitle</name>
+    <value>This is an Apache Sling ESX Scripting Demo</value>
+  </property>
+  <property>
+      <name>navTitle</name>
+      <value>Home</value>
+  </property>
+  <property>
+    <name>content</name>
+    <value># header 1
+      ## header 2</value>
+  </property>
+  <node>
+      <primaryNodeType>sling:Folder</primaryNodeType>
+      <name>posts</name>
+      <node>
+            <primaryNodeType>sling:Folder</primaryNodeType>
+            <name>post1</name>
+            <property>
+                <name>sling:resourceType</name>
+                <value>esx/demo/components/post</value>
+            </property>
+            <property>
+              <name>order</name>
+              <value>100</value>
+            </property>
+                <property>
+                  <name>jcr:title</name>
+                  <value>post one</value>
+                </property>
+                <property>
+                  <name>content</name>
+                  <value>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et  [...]
+## Lorem ipsu
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
+
+Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
+### Lorem ipsu
+Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
+
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis.
+
+### Lorem ipsu
+*At* vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elit [...]
+
+Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita  [...]
+
+Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolo [...]
+
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
+
+Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. </value>
+</property>
+      </node>
+<node>
+            <primaryNodeType>sling:Folder</primaryNodeType>
+            <name>post2</name>
+            <property>
+                <name>sling:resourceType</name>
+                <value>esx/demo/components/post</value>
+            </property>
+                <property>
+                  <name>jcr:title</name>
+                  <value>second blog post</value>
+                </property>
+                <property>
+                  <name>order</name>
+                  <value>50</value>
+                </property>
+
+                <property>
+                  <name>content</name>
+                  <value>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam vehicula, mi id suscipit efficitur, mauris felis porttitor neque, eget tempus metus urna vel urna. Quisque in elit massa. Donec tempus, elit in vulputate auctor, ante arcu feugiat enim, eu condimentum purus leo id metus. In auctor sodales nulla sit amet volutpat. In egestas ex lectus, non dapibus lectus dapibus id. Nullam vulputate nec urna in pretium. Ut mattis sodales rutrum. Morbi a dolor laoreet, au [...]
+## js cloudflare
+
+```javascript
+  render: function () {
+      currentNode.mathVersion = math.VERSION;
+      currentNode.result = math.math(2,2);
+      currentNode.demoResource = demoResource;
+      currentNode.mdcontent = marked(currentNode.properties.content);
+      currentNode.style = stylesheet;
+```
+
+```java
+  class Test {
+    public Test() {
+      super();
+    }
+
+
+  }
+```
+
+> Proin sed pharetra nulla. Proin dui elit, pellentesque ac dolor a, imperdiet fermentum lorem. Integer tempus cursus sagittis. Donec quis aliquet ante. Nunc efficitur elementum consectetur. Maecenas ornare pulvinar feugiat. Nulla iaculis ultrices elit, eu dapibus diam facilisis nec. Pellentesque sit amet augue dui. In nec varius magna. Sed suscipit dignissim mollis. Pellentesque ultricies finibus consectetur. Phasellus dictum ligula mauris, sed dictum ipsum facilisis at. Sed vitae liber [...]
+
+Praesent euismod eget mi nec porta. Donec auctor finibus enim. Pellentesque ut sollicitudin tellus. Fusce placerat aliquet nibh et placerat. Mauris eget metus ipsum. Suspendisse congue elit a justo fringilla, sed varius purus volutpat. Nam ac massa enim. Phasellus posuere, magna sed elementum feugiat, velit ex volutpat quam, et ultricies metus lectus quis lectus.</value>
+</property>
+      </node>
+  </node>
+  <node>
+      <primaryNodeType>sling:Folder</primaryNodeType>
+      <name>pages</name>
+      <node>
+            <primaryNodeType>sling:Folder</primaryNodeType>
+            <name>About</name>
+            <property>
+                <name>sling:resourceType</name>
+                <value>esx/demo/components/post</value>
+            </property>
+            <property>
+              <name>order</name>
+              <value>100</value>
+            </property>
+                <property>
+                  <name>jcr:title</name>
+                  <value>About this Blog</value>
+                </property>
+                <property>
+                  <name>content</name>
+                  <value>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et  [...]
+## Lorem ipsu
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
+
+Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
+### Lorem ipsu
+Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
+
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis.
+
+### Lorem ipsu
+*At* vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elit [...]
+
+Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita  [...]
+
+Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolo [...]
+
+Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
+
+Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. </value>
+</property>
+      </node>
+</node>
+
+
+</node>
diff --git a/src/main/resources/libs/esx/demo/package.json b/src/main/resources/libs/esx/demo/package.json
new file mode 100644
index 0000000..cd3931e
--- /dev/null
+++ b/src/main/resources/libs/esx/demo/package.json
@@ -0,0 +1,17 @@
+{
+  "name": "esxdemo",
+  "version": "1.0.0",
+  "description": "Apache Sling ESX Demo",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "",
+  "license": "ISC",
+  "dependencies": {
+    "handlebars": "^4.0.6",
+    "marked": "^0.3.6",
+    "moment": "^2.18.0",
+    "underscore": "^1.8.3"
+  }
+}
diff --git a/src/main/resources/libs/esx/esx_modules/fs/index.js b/src/main/resources/libs/esx/esx_modules/fs/index.js
new file mode 100644
index 0000000..ae6f28c
--- /dev/null
+++ b/src/main/resources/libs/esx/esx_modules/fs/index.js
@@ -0,0 +1,16 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
diff --git a/src/main/resources/libs/esx/esx_modules/path/index.js b/src/main/resources/libs/esx/esx_modules/path/index.js
new file mode 100644
index 0000000..7f1fdcb
--- /dev/null
+++ b/src/main/resources/libs/esx/esx_modules/path/index.js
@@ -0,0 +1,210 @@
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+The path module provides utilities for working with file and directory paths. It can be accessed using:
+
+const path = require('path');
+@see https://nodejs.org/api/path.html
+
+*/
+// using posix
+
+/**
+ * @see https://nodejs.org/api/path.html#path_path_delimiter
+ */
+var delimiter = ":";
+
+var sep = "/";
+
+/**
+ * @see https://nodejs.org/api/path.html#path_path_basename_path_ext
+ */
+function basename(path, ext) {
+  var startPosition = path.lastIndexOf("/") + 1;
+  var endPosition = (ext) ? (path.length-ext.length) : path.length;
+  return path.substring(startPosition, endPosition);
+}
+
+
+/**
+ * @see https://nodejs.org/api/path.html#path_path_dirname_path
+ */
+function dirname(path) {
+  if(path) {
+    var dirname = "";
+    var parts = path.split("/");
+    var endPosition = (parts[parts.length-1] == 0) ? parts.length-2 : parts.length-1;
+    for(var i=0; i < endPosition; i++){
+      var part = parts[i].trim();
+      if(part.length > 0)
+        dirname += "/" + part;
+    }
+    return dirname;
+  }
+  return ".";
+}
+
+
+/**
+ * @see https://nodejs.org/api/path.html#path_path_extname_path
+ */
+function extname(path) {
+  var lastPosition = path.lastIndexOf(".");
+  var baseName = basename(path);
+  if(lastPosition == -1 || baseName.startsWith("."))  {
+    return "";
+  }
+  return baseName.substring(baseName.lastIndexOf("."), baseName.length);
+}
+
+/*
+@see https://nodejs.org/api/path.html#path_path_format_pathobject
+pathObject <Object>
+dir <String>
+root <String>
+base <String>
+name <String>
+ext <String>
+Returns: <String>
+*/
+function format(pathObject){
+  var dir = pathObject.dir;
+  var root = pathObject.root;
+  var base = pathObject.base;
+  var name = pathObject.name;
+  var ext = pathObject.ext;
+  var first = dir;
+  var middle = sep;
+  var end = base;
+
+  if(dir && root && base) {
+    first = dir;
+    middle = sep;
+    end = base;
+  }
+  if(!dir) {
+      first = root;
+  }
+
+  if(!dir || dir == root) {
+    middle = "";
+  }
+
+  if(!base) {
+    middle = "";
+    end = name + ext;
+  }
+
+  return  first.middle.end;// for es6: `${first}${middle}${end}`
+}
+
+
+/**
+ * @see https://nodejs.org/api/path.html#path_path_isabsolute_path
+ */
+function isAbsolute(path) {
+  if(path && path.length == 0) {
+    return false;
+  }
+
+  return path.startsWith("/");
+}
+
+/**
+ * @see https://nodejs.org/api/path.html#path_path_join_paths
+ */
+function join() {
+  var path = "";
+  var max = arguments.length - 1;
+  for(key in arguments) {
+    var value = arguments[key];
+
+    if((value instanceof Object)) {
+      throw "TypeError: Arguments to path.join must be strings"
+    }
+    if(value && value.length > 0) {
+      path +=  value;
+      if(key < max) {
+        path += sep;
+      }
+    }
+  }
+  return (path.length == 0) ? "." : normalize(path);
+}
+
+
+/**
+ * @see https://nodejs.org/api/path.html#path_path_normalize_path
+ */
+function normalize(path) {
+  if(path.length == 0) {
+    return ".";
+  }
+
+  var parts = path.split(sep);
+  var normalizedPath = "";
+  for(i in parts) {
+    var dir = parts[i];
+    var a = parseInt(i) + 1;
+
+
+    if(parts[a] == ".." || dir == ".." || dir == ".") {
+      i=i+2;
+      continue;
+    }
+
+    if(dir.length > 0 && dir != sep) {
+      normalizedPath += sep;
+      normalizedPath += dir;
+    }
+  }
+  return normalizedPath;
+}
+
+
+/*
+
+path.parse(path)
+path.posix
+path.relative(from, to)
+path.resolve([...paths])
+
+*/
+
+exports.basename = basename;
+exports.delimiter = delimiter;
+exports.dirname = dirname;
+exports.extname = extname;
+exports.format = format;
+exports.sep = sep;
+exports.isAbsolute = isAbsolute;
+exports.join = join;
+exports.normalize = normalize;
+
+exports.posix = {};
+exports.posix.basename = basename;
+exports.posix.delimiter = delimiter;
+exports.posix.dirname = dirname;
+exports.posix.extname = extname;
+
+exports.win32 = {};
+// actually not implemented yet
+exports.win32.basename = basename;
+exports.win32.delimiter = delimiter;
+exports.win32.dirname = dirname;
+exports.win32.extname = extname;
\ No newline at end of file
diff --git a/src/main/resources/libs/esx/esx_modules/url/index.js b/src/main/resources/libs/esx/esx_modules/url/index.js
new file mode 100644
index 0000000..0a69924
--- /dev/null
+++ b/src/main/resources/libs/esx/esx_modules/url/index.js
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * URL Module
+ * only Sling specific functions are implemented from:
+ * org.apache.sling.api.request Interface RequestPathInfo
+ *
+ * needs to be extended to support full: https://nodejs.org/api/url.html
+ * This Function should return an annotated URL element including
+ * sling specific informatin like suffix, resourcePath, selectors, selector string
+ * @param link (location.href for instance or resoruce path)
+ * @returns {URL}
+ * not implemented yet.
+ * adopt: https://github.com/labertasch/slingui/blob/master/stream/src/main/resources/SLING-INF/libs/slingui/client/src/utils/URL.js
+ */
+
+ function getAbsoluteParent(path, level){
+     var idx = 0;
+     var len = path.length;
+     while (level >= 0 && idx < len) {
+            idx = path.indexOf('/', idx + 1);
+            if (idx < 0) {
+                  idx = len;
+             }
+            level--;
+      }
+     return level >= 0 ? "" : path.substring(0, idx);
+ }
+
+
+ function getRelativeParent(path, level) {
+     var idx = path.length;
+     while (level > 0) {
+             idx = path.lastIndexOf('/', idx - 1);
+            if (idx < 0) {
+                     return "";
+                 }
+             level--;
+         }
+     return (idx == 0) ? "/" : path.substring(0, idx);
+ }
+
+ function getRelativeParent(path, level) {
+     var idx = path.length;
+     while (level > 0) {
+             idx = path.lastIndexOf('/', idx - 1);
+            if (idx < 0) {
+                     return "";
+                 }
+             level--;
+         }
+     return (idx == 0) ? "/" : path.substring(0, idx);
+ }
+
+exports.getAbsoluteParent = getAbsoluteParent;
+exports.getRelativeParent = getRelativeParent;

-- 
To stop receiving notification emails like this one, please contact
rombert@apache.org.