Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eslintify panel #35

Merged
merged 7 commits into from
Jun 13, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Expand Up @@ -5,6 +5,7 @@ node_js:
before_script:
- npm run build
- npm run build-test
- npm run lint
script:
- npm run test-server
- npm run test-browser-sauce
Expand Down
4 changes: 2 additions & 2 deletions lib/component-utils/proxy-component.js
Expand Up @@ -9,7 +9,7 @@ import {h} from '../dom-patcher';
* @extends Component
*/
class ProxyComponent extends Component {
get config(){
get config() {
return Object.assign({
template: ({$component}) => {
return h($component.getTargetElementTag(), {
Expand Down Expand Up @@ -72,7 +72,7 @@ class ProxyComponent extends Component {
* }
* }
*/
allowEvent(ev) {
allowEvent(ev) { // eslint-disable-line no-unused-vars
return true;
}

Expand Down
51 changes: 25 additions & 26 deletions lib/component.js
@@ -1,8 +1,7 @@
import cuid from 'cuid';
import pick from 'lodash.pick';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yikes! dead weight

import WebComponent from 'webcomponent';

import { EMPTY_DIV, DOMPatcher, h } from './dom-patcher';
import {EMPTY_DIV, DOMPatcher, h} from './dom-patcher';
import Router from './router';

const DOCUMENT_FRAGMENT_NODE = 11;
Expand Down Expand Up @@ -108,7 +107,7 @@ class Component extends WebComponent {
*/
findPanelParentByTagName(tagName) {
tagName = tagName.toLowerCase();
for (let node = this.$panelParent; !!node; node = node.$panelParent) {
for (let node = this.$panelParent; node; node = node.$panelParent) {
if (node.tagName.toLowerCase() === tagName) {
return node;
}
Expand Down Expand Up @@ -166,7 +165,7 @@ class Component extends WebComponent {
* return state.largeResultSetID !== this._cachedResultID;
* }
*/
shouldUpdate(state) {
shouldUpdate(state) { // eslint-disable-line no-unused-vars
return true;
}

Expand Down Expand Up @@ -202,10 +201,10 @@ class Component extends WebComponent {

this.panelID = cuid();
this._config = Object.assign({}, {
css: '',
css: ``,
helpers: {},
routes: {},
template: () => { throw Error('No template provided by Component subclass'); },
template: () => { throw Error(`No template provided by Component subclass`); },
updateSync: false,
useShadowDom: false,
}, this.config);
Expand All @@ -222,13 +221,13 @@ class Component extends WebComponent {
this.isStateShared = false;
}

if (this.getConfig('useShadowDom')) {
this.el = this.attachShadow({mode: 'open'});
this.styleTag = document.createElement('style');
this.styleTag.innerHTML = this.getConfig('css');
if (this.getConfig(`useShadowDom`)) {
this.el = this.attachShadow({mode: `open`});
this.styleTag = document.createElement(`style`);
this.styleTag.innerHTML = this.getConfig(`css`);
this.el.appendChild(this.styleTag);
} else if (this.getConfig('css')) {
throw Error('"useShadowDom" config option must be set in order to use "css" config.');
} else if (this.getConfig(`css`)) {
throw Error(`"useShadowDom" config option must be set in order to use "css" config.`);
} else {
this.el = this;
}
Expand All @@ -248,11 +247,11 @@ class Component extends WebComponent {

this.$panelChildren = new Set();

if (typeof this.$panelParentID !== 'undefined') {
if (typeof this.$panelParentID !== `undefined`) {
this.isPanelChild = true;
// find $panelParent
for (let node = this.parentNode; node && !this.$panelParent; node = node.parentNode) {
if (node.nodeType === DOCUMENT_FRAGMENT_NODE) { // handle shadow-root
if (node.nodeType === DOCUMENT_FRAGMENT_NODE) { // handle shadow-root
node = node.host;
}
if (node.panelID === this.$panelParentID) {
Expand Down Expand Up @@ -280,20 +279,20 @@ class Component extends WebComponent {

const newState = Object.assign(
{},
this.getConfig('defaultState'),
this.getConfig(`defaultState`),
this.state,
this.getJSONAttribute('data-state'),
this.getJSONAttribute(`data-state`),
this._stateFromAttributes()
);
Object.assign(this.state, newState);

if (Object.keys(this.getConfig('routes')).length) {
if (Object.keys(this.getConfig(`routes`)).length) {
this.router = new Router(this, {historyMethod: this.historyMethod});
this.navigate(window.location.hash);
}

this.domPatcher = new DOMPatcher(this.state, this._render.bind(this), {
updateMode: this.getConfig('updateSync') ? 'sync': 'async',
updateMode: this.getConfig(`updateSync`) ? `sync`: `async`,
});
this.el.appendChild(this.domPatcher.el);
this.initialized = true;
Expand All @@ -320,7 +319,7 @@ class Component extends WebComponent {
}

attributeChangedCallback(attr, oldVal, newVal) {
if (attr === 'style-override') {
if (attr === `style-override`) {
this._applyStyles(newVal);
}
if (this.isPanelRoot && this.initialized) {
Expand All @@ -329,8 +328,8 @@ class Component extends WebComponent {
}

_applyStyles(styleOverride) {
if (this.getConfig('useShadowDom')) {
this.styleTag.innerHTML = this.getConfig('css') + (styleOverride || '');
if (this.getConfig(`useShadowDom`)) {
this.styleTag.innerHTML = this.getConfig(`css`) + (styleOverride || ``);
}
}

Expand All @@ -341,20 +340,20 @@ class Component extends WebComponent {
toString() {
try {
return `${this.tagName}#${this.panelID}`;
} catch(e) {
return 'UNKNOWN COMPONENT';
} catch (e) {
return `UNKNOWN COMPONENT`;
}
}

_render(state) {
if (this.shouldUpdate(state)) {
try {
this._rendered = this.getConfig('template')(Object.assign({}, state, {
this._rendered = this.getConfig(`template`)(Object.assign({}, state, {
$app: this.appState,
$component: this,
$helpers: this.helpers,
}));
} catch(e) {
} catch (e) {
this.logError(`Error while rendering ${this.toString()}`, this, e.stack);
}
}
Expand Down Expand Up @@ -411,7 +410,7 @@ class Component extends WebComponent {
} else {

// update DOM, router, descendants etc.
const updateHash = '$fragment' in stateUpdate && stateUpdate.$fragment !== this[store].$fragment;
const updateHash = `$fragment` in stateUpdate && stateUpdate.$fragment !== this[store].$fragment;
const cascadeFromRoot = cascade && !this.isPanelRoot;
const updateOptions = {cascade, store};
const rootOptions = {exclude: this, cascade, store};
Expand Down
26 changes: 13 additions & 13 deletions lib/dom-patcher.js
Expand Up @@ -7,12 +7,12 @@
import snabbdom from 'snabbdom';
import h from 'snabbdom/h';

import snabbAttributes from 'snabbdom/modules/attributes';
import snabbDataset from 'snabbdom/modules/dataset';
import snabbDelayedClass from 'snabbdom-delayed-class';
import snabbAttributes from 'snabbdom/modules/attributes';
import snabbDataset from 'snabbdom/modules/dataset';
import snabbDelayedClass from 'snabbdom-delayed-class';
import snabbEventlisterners from 'snabbdom/modules/eventlisteners';
import snabbProps from 'snabbdom/modules/props';
import snabbStyle from 'snabbdom/modules/style';
import snabbProps from 'snabbdom/modules/props';
import snabbStyle from 'snabbdom/modules/style';

const patch = snabbdom.init([
snabbAttributes,
Expand All @@ -23,24 +23,24 @@ const patch = snabbdom.init([
snabbStyle,
]);

export const EMPTY_DIV = h('div');
export const EMPTY_DIV = h(`div`);
export {h};

export class DOMPatcher {
constructor(initialState, renderFunc, options={}) {
this.updateMode = options.updateMode || 'async';
this.updateMode = options.updateMode || `async`;

this.state = Object.assign({}, initialState);
this.renderFunc = renderFunc;
this.vnode = this.renderFunc(this.state);

// prepare root element
const tagName = this.vnode.sel.split(/[#\.]/)[0];
const classMatches = this.vnode.sel.match(/\.[^\.#]+/g);
const idMatch = this.vnode.sel.match(/#[^\.#]+/);
const tagName = this.vnode.sel.split(/[#.]/)[0];
const classMatches = this.vnode.sel.match(/\.[^.#]+/g);
const idMatch = this.vnode.sel.match(/#[^.#]+/);
this.el = document.createElement(tagName);
if (classMatches) {
this.el.className = classMatches.map(c => c.slice(1)).join(' ');
this.el.className = classMatches.map(c => c.slice(1)).join(` `);
}
if (idMatch) {
this.el.id = idMatch[0].slice(1);
Expand All @@ -56,13 +56,13 @@ export class DOMPatcher {

this.pendingState = newState;
switch (this.updateMode) {
case 'async':
case `async`:
if (!this.pending) {
this.pending = true;
requestAnimationFrame(() => this.render());
}
break;
case 'sync':
case `sync`:
this.render();
break;
}
Expand Down
7 changes: 4 additions & 3 deletions lib/isorender/dom-shims.js
@@ -1,3 +1,4 @@
/* global global */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be eslint-env node?

/**
* Node.js polyfill for rendering Panel components without a browser.
* Makes the following objects globally available:
Expand Down Expand Up @@ -27,7 +28,7 @@ import requestAnimationFrame from 'raf';
global.requestAnimationFrame = global.requestAnimationFrame || requestAnimationFrame;

// patch DOM insertion functions to call connectedCallback on Custom Elements
['appendChild', 'insertBefore', 'replaceChild'].forEach(funcName => {
[`appendChild`, `insertBefore`, `replaceChild`].forEach(funcName => {
const origFunc = Element.prototype[funcName];
Element.prototype[funcName] = function() {
const child = origFunc.apply(this, arguments);
Expand Down Expand Up @@ -76,7 +77,7 @@ Document.prototype.createElement = function(tagName) {
el = originalCreateElement(...arguments);
}
return el;
}
};

global.customElements = global.customElements || {
define(tagName, proto) {
Expand All @@ -86,5 +87,5 @@ global.customElements = global.customElements || {
} else {
registeredElements[tagName] = proto;
}
}
},
};
32 changes: 16 additions & 16 deletions lib/router.js
@@ -1,31 +1,35 @@
function stripHash(fragment) {
return fragment.replace(/^#*/, ``);
}

// just the necessary bits of Backbone router+history
export default class Router {
constructor(app, options={}) {
// allow injecting window dep
const routerWindow = this.window = options.window || window;

this.app = app;
const routeDefs = this.app.getConfig('routes');
const routeDefs = this.app.getConfig(`routes`);

// https://github.com/jashkenas/backbone/blob/d682061a/backbone.js#L1476-L1479
// Cached regular expressions for matching named param parts and splatted
// parts of route strings.
const optionalParam = /\((.*?)\)/g;
const namedParam = /(\(\?)?:\w+/g;
const splatParam = /\*\w+/g;
const escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
const namedParam = /(\(\?)?:\w+/g;
const splatParam = /\*\w+/g;
const escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; // eslint-disable-line no-useless-escape
this.compiledRoutes = Object.keys(routeDefs).map(routeExpr => {
// https://github.com/jashkenas/backbone/blob/d682061a/backbone.js#L1537-L1547
let expr = routeExpr
.replace(escapeRegExp, '\\$&')
.replace(optionalParam, '(?:$1)?')
.replace(namedParam, (match, optional) => optional ? match : '([^/?]+)')
.replace(splatParam, '([^?]*?)');
expr = new RegExp('^' + expr + '(?:\\?([\\s\\S]*))?$');
.replace(escapeRegExp, `\\$&`)
.replace(optionalParam, `(?:$1)?`)
.replace(namedParam, (match, optional) => optional ? match : `([^/?]+)`)
.replace(splatParam, `([^?]*?)`);
expr = new RegExp(`^` + expr + `(?:\\?([\\s\\S]*))?$`);

// hook up route handler function
let handler = routeDefs[routeExpr];
if (typeof handler === 'string') {
if (typeof handler === `string`) {
// reference to another handler rather than its own function
handler = routeDefs[handler];
}
Expand All @@ -34,9 +38,9 @@ export default class Router {
});

const navigateToHash = () => this.navigate(routerWindow.location.hash);
routerWindow.addEventListener('popstate', () => navigateToHash());
routerWindow.addEventListener(`popstate`, () => navigateToHash());

this.historyMethod = options.historyMethod || 'pushState';
this.historyMethod = options.historyMethod || `pushState`;
const origChangeState = routerWindow.history[this.historyMethod];
routerWindow.history[this.historyMethod] = function() {
origChangeState.apply(routerWindow.history, arguments);
Expand Down Expand Up @@ -89,7 +93,3 @@ export default class Router {
}
}
}

function stripHash(fragment) {
return fragment.replace(/^#*/, '');
}
5 changes: 3 additions & 2 deletions package.json
Expand Up @@ -12,6 +12,7 @@
"build": "babel lib -d build && cp -r build/isorender .",
"build-test": "webpack --config test/browser/webpack.config.js",
"docs": "rm -rf docs && jsdoc lib lib/component-utils lib/isorender -t node_modules/minami -R README-API.md -d docs",
"lint": "eslint devtools hot lib scripts test --ignore-pattern 'test/browser/build/'",
"prepublishOnly": "npm run build",
"publish-devtools": "node scripts/publish-devtools.js",
"test": "npm run build-test && npm run test-server && npm run test-browser-local",
Expand Down Expand Up @@ -58,8 +59,8 @@
"babel-preset-es2015": "^6.6.0",
"chai": "^3.5.0",
"chrome-store-api": "^1.0.5",
"eslint": "4.12.1",
"eslint-config-mixpanel": "3.1.0",
"eslint": "4.18.1",
"eslint-config-mixpanel": "3.5.0",
"jsdoc": "^3.5.5",
"minami": "^1.1.1",
"mocha": "^2.5.3",
Expand Down
2 changes: 1 addition & 1 deletion scripts/publish-devtools.js
Expand Up @@ -22,7 +22,7 @@ const manifestPath = `${devtoolsDir}/manifest.json`;

// Main wrapped in async since top-level await is not supported
(async function() {
try{
try {
// Write new version to manifest
const manifest = JSON.parse(fs.readFileSync(manifestPath, `utf-8`));
console.log(`Current manifest:\n`, manifest);
Expand Down
2 changes: 1 addition & 1 deletion test/browser/component-utils/proxy-component.js
Expand Up @@ -19,7 +19,7 @@ describe(`ProxyComponent`, function() {
});

it(`re-dispatches events from child that are not composed`, function(done) {
el.addEventListener(`nonComposedEvent`, () => done())
el.addEventListener(`nonComposedEvent`, () => done());
el.setAttribute(`send-non-composed`, ``);
});

Expand Down
7 changes: 4 additions & 3 deletions test/fixtures/attr-reflection-app.js
@@ -1,10 +1,11 @@
import { Component, h } from '../../lib';
import {Component, h} from '../../lib';

export class AttrReflectionApp extends Component {
get config() {
return {
template: state => h('div', {class: {'attr-app': true}}, [
h('p', `Value of attribute wombats: ${this.getAttribute('wombats')}`),
// eslint-disable-next-line no-unused-vars
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just a test fixture, might as well make it conform with template: () => h(...) (ditto for the other fixtures below)

template: state => h(`div`, {class: {'attr-app': true}}, [
h(`p`, `Value of attribute wombats: ${this.getAttribute(`wombats`)}`),
]),
};
}
Expand Down