<file_sep>const path = require('path');
const glob = require('glob');

const resolverFileGlob = '**/*.js';
const CWD = process.cwd();
const slash_REG = /\/$/;

/*
 * PageMapper will require all the files in a folder and its sub folders and let you use `findPage` to access them
 * This makes it easy to setup a single route to manage all your pages.
 * You can also setup a default 404 page here
 */
module.exports = class PageMapper {
  constructor(uri, ignore = []) {
    const resolverFiles = path.join(uri, resolverFileGlob);
    const jsFiles = glob.sync(resolverFiles, { ignore: ignore }) || [];
    this.modules = jsFiles.reduce((a, f) => {
      const convertedURL = convertToUrl(f, uri);
      a[convertedURL] = require(path.join(CWD, f));
      if (convertedURL.includes('404')) this._404 = a[convertedURL];
      return a;
    }, {});
  }

  findPage(url) {
    if (this._root && url === '/') url = '/' + this._root;
    const module = this.modules[url.replace(slash_REG, '')];
    if (typeof module !== 'function' && typeof module !== 'object') return this._404 || noop;
    return this.modules[url.replace(slash_REG, '')];
  }

  set pageNotFount(url) {
    this._404 = this.modules[url.replace(slash_REG, '')];
  }

  set root(url) {
    this._root = url;
  }
};

function convertToUrl(str, uri) {
  return str
    .replace(uri, '') // remove folder path
    .replace('index.js', '') // do not use index.js. insterad use folder
    .replace('.js', '') //js extension
    .replace(slash_REG, ''); // trailing slash
}

function noop() {
  return {};
}
<file_sep>/*
 * Cache
 * This is inteded to increase performance
 * Using caching can make perfance for serving the website close to the performance of using static files
 * The one caveot is the first time you render a page after the server being started it will not be optomized yet
 * The cache is stored in memory
 */

// TODO look into alowing cache to be stored and managed outside of memory

exports.memoize = fn => {
  return variadic.bind(
    this,
    fn,
    new Cache()
  );
};

/*
 * https://en.wikipedia.org/wiki/Variadic_function
 * This just means it is expeting multiple arguments, but it will work with 0 and 1
 */
function variadic(fn, cache) {
  const args = Array.prototype.slice.call(arguments, 2);
  const cacheKey = JSON.stringify(args);
  let computedValue = cache.get(cacheKey);
  if (typeof computedValue === 'undefined') {
    computedValue = fn.apply(this, args);
    cache.set(cacheKey, computedValue);
  }
  return computedValue;
}

// Basic in memory cache class
class Cache {
  constructor() {
    this.cache = Object.create(null);
  }

  has(key) {
    return (key in this.cache);
  }

  get(key) {
    return this.cache[key];
  }

  set(key, value) {
    this.cache[key] = value;
  }
}
<file_sep>const customElements = require('./src/customElements');
const PageMapper = require('./src/PageMapper');
const fileHandler = require('./src/fileHandler');
const { set } = require('./src/config');
const { css, html } = require('./src/template-literal-tags');
const HTMLElementExtended = require('./src/HTMLElementExtended');
const Page = require('./src/Page');

module.exports = {
  customElements,
  PageMapper,
  html,
  css,
  setConfig: set,
  fileHandler,
  HTMLElementExtended,
  Page
};
<file_sep>module.exports = {
  css: basic,
  html: basic
};

// this just builds the remplate literal
// Basically this will enable colors in your editor
function basic(strings, ...expressionValues) {
  let finalString = '';
  let i = 0;
  let length = strings.length;
  for(; i < length; i++) {
    if (i > 0) finalString += expressionValues[i - 1];
    finalString += strings[i];
  }
  return finalString;
}
<file_sep>module.exports = class HTMLElementExtended {
  constructor() {
    this.style = {};
  }
  appendChild() {}
  attachShadow() {
    this.shadowRoot = new ShadowRoot();
  }
  hasAttribute() {}
  getAttribute() {}
  setAttribute() {}
  removeAttribute() {}
  addEventListener() {}
  removeEventListener() {}
  getAttribute() { return ''; }
  hasAttribute() {}
  setAttribute() {}

  // override methods
  html() { return ''; }
  css() { return ''; }
  externalCSS() { return ''; }

  // client methods
  // do not override
  render() {}
  cloneTemplate() {}

  // building methods
  getTemplateElementAsIIFE(name) {
    return `(function(){
      var t=document.createElement('template');
      t.setAttribute('id','${name}');
      t.innerHTML=\`
      <style>
        ${this.css()}
      </style>
      <render-block>
        ${this.html()}
      </render-block>
      \`;
      document.body.insertAdjacentElement('beforeend', t);
    }());`;
  }

  getClassAsString(name) {
    let cstr = this.constructor.toString();
    // remove class declaration
    cstr = cstr.slice(cstr.indexOf('{') + 1, cstr.lastIndexOf('}'));

    return `
customElements.define("${name}", class extends HTMLElement {
  ${cstr}

  render() {
    const renderBlock = document.querySelector('render-block');
    if (!renderBlock) throw Error('Could not find <render-block>');
    renderBlock.innerHTML = this.html();
  }

  cloneTemplate() {
    var template = document.getElementById('${name}');
    var templateContent = template.content;
    var shadowRoot = this.shadowRoot ? this.shadowRoot : this.attachShadow({mode: 'open'});
    shadowRoot.appendChild(templateContent.cloneNode(true));
  }
});
    `;
  }
};

class ShadowRoot {
  innerHTML() {}
}
<file_sep># Attention: project moved and renamed
This project has been moved under [webformula](https://github.com/webformula) and renamed to [pax-core](https://github.com/webformula/pax-core)
<file_sep>// Global Configuration

const config = {
  /*
   * This will memoize certain methods to prevent unnecessary processing
   * This is essantially equal to static file performance after the first request
   */
  memoize: true,

  /*
   * default: true
   * user built in service worker to manage cache
   */
  serviceWorker: true
};

exports.get = (name) => name !== undefined ? config[name] : config;
exports.set = (params = {}) => {
  Object.keys(config).forEach(key => {
    if (params[key] !== undefined) config[key] = params[key];
  });
};
<file_sep>const config = require('./config');
const { memoize } = require('./cache');
const components = {};

module.exports = {
  define(name, constructor) {
    if (components[name]) throw Error(`component "${name}" has already been registered. Please change the components name`);
    components[name] = constructor;
  },

  getStaticExternalCSS() {
    if (this.memoize) {
      if (!this.getStaticExternalCSSMemoized) this.getStaticExternalCSSMemoized = memoize(_getStaticExternalCSS);
      return this.getStaticExternalCSSMemoized();
    }
    return _getStaticExternalCSS();
  },

  getStaticFile() {
    if (this.memoize) {
      if (!this.getStaticFileMemoized) this.getStaticFileMemoized = memoize(_getStaticFile);
      return this.getStaticFileMemoized();
    }
    return _getStaticFile();
  }
};

function _getStaticExternalCSS() {
  return Object
    .keys(components)
    .map(key => new components[key]().externalCSS())
    .join('\n');
}

function _getStaticFile() {
  return `
    document.addEventListener("DOMContentLoaded", function (event) {
      ${Object
        .keys(components)
        .map(key => {
          const comp = new components[key]();
          return [
            comp.getTemplateElementAsIIFE(key),
            comp.getClassAsString(key)
          ].join('\n');
        })
        .join('\n')}
    });
  `;
}
<file_sep>module.exports = () => `
  var _cacheName = 'wcn-cache';

  self.addEventListener('install', function (event) {
    event.waitUntil(
      caches.open(_cacheName).then(function (cache) {
        return cache.addAll(
          [
            // TODO get list of html css and javascript
            // can use page mapper and then parse links and scripts
          ]
        );
      })
    );
  });

  self.addEventListener('activate', function (event) {
    event.waitUntil(
      caches.keys().then(function (cacheNames) {
        return Promise.all(
          cacheNames.filter(function (cacheName) {
            return cacheName === _cacheName;
          }).map(function (cacheName) {
            return caches.delete(cacheName);
          })
        );
      })
    );
  });


  self.addEventListener('fetch', function (event) {
    event.respondWith(
      caches.open(_cacheName).then(function (cache) {
        return cache.match(event.request).then(function (response) {
          return response || fetch(event.request).then(function (response) {
            if (!event.request.url.includes('load-service-worker.js')) cache.put(event.request, response.clone());
            return response;
          }).catch(function() {
            return caches.match('/offline.html');
          }).catch(function() {
            return new Response("<h2>No page loaded</h2><p>You are currently offline and the page is not cahced, Please check you internet and reload to access this page.</p>", {
              headers: {'Content-Type': 'text/html'}
            });
          });
        });
      }).catch(function (e) {
        throw e;
      })
    );
  });
`;
<file_sep>const components = require('../componentRegistry');
const files = {
  main_js: require('./main.js'),
  serviceWorkerLoader_js: require('./service-worker-loader'),
  serviceWorker_js: require('./service-worker'),
  components_js: components.staticFile,
  components_css: components.staticComponentCSS
};
const reg = /-([a-z])/g;

function toCamelCase(str) {
  return str.replace(reg, g => g[1].toUpperCase());
}

module.exports = params => {
  const cameled = toCamelCase(params.fileName.replace('.js', '_js').replace('.css', '_css'));
  if (!files[cameled]) throw Error('file not found');
  return {
    content: files[cameled](params),
    mime: getMime(params.fileName)
  };
};

function getMime(file) {
  if (file.includes('.js')) return 'text/javascript';
  if (file.includes('.css')) return 'text/css';
  if (file.includes('.html')) return 'text/html';
  return 'text/plain';
}


exports.getJavascript = () => {
  
}
<file_sep>module.exports = params => `
  window.html = function (strings, ...expressionValues) {
    let finalString = '';
    let i = 0;
    let length = strings.length;
    for(; i < length; i++) {
      if (i > 0) finalString += expressionValues[i - 1];
      finalString += strings[i];
    }
    return finalString;
  };
  window.htmlSafe = window.html;
  window.css = window.html;
`;
