commit cd2dccaf5ca4d354a7cea69682999c210a1c87c6
parent ec59b356dd842e90703061e2b69c9540d2379fbe
Author: finwo <finwo@pm.me>
Date: Wed, 28 Oct 2020 16:50:39 +0100
First version
Diffstat:
7 files changed, 193 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
@@ -0,0 +1,78 @@
+# fwebc
+
+Simple web component framework to teach myself about web components
+
+## Install
+
+This package is intended for direct use in the browser, although wrappers like
+browserify and/or webpack *should* work.
+
+```js
+npm install --save fwebc
+```
+
+```html
+<script src="https://unpkg.com/fwebc"></script>
+```
+
+Upon load, fwebc returns within `module.exports` if support for that is
+detected, else it attaches itself to `window.fwebc`.
+
+## Usage
+
+fwebc makes use of `window.customElements`, so initialization is not needed,
+only configuration.
+
+### fwebc.cfg( configuration? )
+
+For now, only the default file extension and the base uri (location to your
+templates) are configurable.
+
+```js
+fwebc.cfg({
+ ext : 'tag',
+ base: '/partial',
+});
+```
+
+### fwebc.install( callback:fn <component> )
+
+Adds a callback to the creation process of an element. The shadow root (a.k.a.
+component) is given as the first -and only- argument to the callback.
+
+This functionality allows you to add mixins or other features to components.
+
+### fwebc.uninstall( callback )
+
+When given the exact same callback as you installed, removes it from the calls
+to perform upon component creation.
+
+### fwebc.register(name, source)
+
+Registers a new component, based on the source string you give. The name must
+include a hyphen `-` because fwebc is built directly on top of
+`window.customElements`.
+
+**CAUTION**: this method will **NOT** throw an error if a name has already been
+registered.
+
+### fwebc.load(name)
+
+Loads the template identified by the given name, surrounded by the configured
+`base` and `ext`, and registers it.
+
+## Templates
+
+Loaded templates are attached to elements as shadow roots, with the addition of
+all top-layer script tags being executed having the shadow root as their `this`
+variable.
+
+Data binding is not included by default, but is easily added by including a
+module like [rivets](https://www.npmjs.com/package/rivets) and installing it
+like a plugin as follows:
+
+```js
+fwebc.install(component => {
+ rivets.bind(component, component);
+});
+```
diff --git a/index.js b/index.js
@@ -0,0 +1,83 @@
+;(factory => {
+ if (('object' === typeof module) && ('exports' in module)) {
+ module.exports = factory({
+ fetch: require('node-fetch'),
+ });
+ } else if ('object' === typeof window) {
+ window.fwebc = factory({
+ fetch: window.fetch,
+ });
+ }
+})(({fetch}) => {
+ const fwebc = {};
+ const plugins = [];
+ const config = {
+ ext: 'tag',
+ base: '/partial',
+ };
+
+ // Override configs
+ fwebc.cfg = cfg => {
+ Object.assign(config, cfg);
+ };
+
+ // Install a plugin
+ fwebc.install = callback => {
+ if ('function' !== typeof callback) return;
+ plugins.push(callback);
+ };
+
+ // Remove a plugin
+ fwebc.uninstall = callback => {
+ const idx = plugins.indexOf(callback);
+ if (!~idx) return;
+ plugins.splice(idx, 1);
+ };
+
+ // Register a component
+ fwebc.register = (name, source) => {
+ if (window.customElements.get(name)) return;
+ window.customElements.define(name, class extends HTMLElement {
+ constructor() {
+ super();
+
+ // Build shadowroot
+ let template = document.createElement('template');
+ template.innerHTML = source;
+ if (template.content.firstChild instanceof HTMLTemplateElement) {
+ template = template.content.firstChild;
+ }
+ const shadow = this.attachShadow({ mode: 'open' });
+ shadow.appendChild(template.content);
+
+ // Run plugins
+ for (const plugin of plugins) {
+ plugin(shadow);
+ }
+
+ // Run component code
+ (new Function([...shadow.children]
+ .filter(el => el instanceof HTMLScriptElement)
+ .map(el => el.innerHTML)
+ .join('')
+ )).call(shadow);
+
+ // Load dependencies
+ if (shadow.dependencies) {
+ dependencies.forEach(fwebc.load);
+ }
+ }
+ });
+ };
+
+ // Load a component
+ fwebc.load = name => {
+ fetch(`${config.base}/${name}.${config.ext}`)
+ .then(res => res.text())
+ .then(source => {
+ fwebc.register(name, source);
+ });
+ };
+
+ return fwebc;
+});
diff --git a/package.json b/package.json
@@ -4,7 +4,8 @@
"description": "",
"main": "index.js",
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "test": "echo \"Error: no test specified\"",
+ "postpublish": "PACKAGE_VERSION=$(cat package.json | grep \\\"version\\\" | head -1 | awk -F: '{print $2}' | sed 's/[\",]//g' | tr -d '[[:space:]]') && npm deprecate \"fwebc@<${PACKAGE_VERSION}\" \"Rolling release, please update to ${PACKAGE_VERSION}\""
},
"repository": {
"type": "git",
diff --git a/test/assets/fwebc.js b/test/assets/fwebc.js
@@ -0,0 +1 @@
+../../index.js
+\ No newline at end of file
diff --git a/test/assets/style.css b/test/assets/style.css
diff --git a/test/index.html b/test/index.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <link rel="stylesheet" href="/assets/style.css">
+ </head>
+ <body>
+ <my-app></my-app>
+ <script src="/assets/fwebc.js"></script>
+ <script>
+ (async () => {
+ fwebc.cfg({
+ base: '/partial',
+ ext : 'tag',
+ });
+ fwebc.load('my-app');
+ })();
+ </script>
+ </body>
+</html>
diff --git a/test/partial/my-app.tag b/test/partial/my-app.tag
@@ -0,0 +1,8 @@
+<template>
+ <h2>Bird is the word!</h2>
+ <style>
+ h2 {
+ color: #555;
+ }
+ </style>
+</template>