npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

xypts

v1.0.0-preA3

Published

Xyp - Minimalistic TypeScript framework

Downloads

12

Readme

Xyp - minimalistic TypeScript framework

npm version Codacy Badge

Table of Contents

Quick start

Install this package:

npm i -g xypts

Create new project:

xyp new quick-start

Start development server:

xyp serve

Edit src/components/app-root and open browser in http://localhost:9000

CLI

For CLI usage you can use command xyp

Reference

Components

You can create components with custom template and css.

xyp component my-awesome-component

Shorthand: xyp c my-awesome-component

New folder called my-awesome-component will be created. Now you can use it in app-root.html:

<my-awesome-component></my-awesome-component>

Attributes [directives]

(bind) - Two way binding

Example:

<h1 (bind)="name"></h1>
<input (bind)="name">

In this example h1 inner text depends on variable "name", which is binded to the input.

(listen) - One way binding

<h1 (listen)="name"></h1>
<input (bind)="name">

In this example header listens for changes in "name" variable. Works same as previous example.

(listen:<attribute>), (bind:<attribute>) - Data binding

<img (listen:src)="myphoto">
<input (listen:placeholder)="placeholder">

These attributes bind variable to defined attribute of calling element.

(model) - One way binding

<h1 (listen)="name"></h1>
<input (model)="name">

This example's input listens for changes and only modifies "name" variable.

(init) - Initialize value

<h1 (listen)="name"></h1>
<input (init)="name = 'hi there!'" (bind)="name">

In this example "name" varibale is initialized with value 'hi there!'. Note: if variable was already initialized(e.g. is not undefined), it won't be initialized

(for) - Iterate over array or object

Example:

<div (init)="favourites = ['snickers', 'milky way']" (for)="candy of favourites"><div>I love ${candy}!</div></div>

"favourites" variable is initalized with ['snickers', 'milky way'] array, for directive iterates over array's values. Use "in" instead of "of" to iterate over object's keys, not values. To access iterated value, use variable name in "${}" in html, e.g. ${candy}. Notice that scope of "${}" value is isolated. That means you can access only this variable and json() function and i, e.g. iterated index.

json - Convert object to JSON

Usage:

<div (init)="favourites = [{'snickers': true}, {'milky way': false}]" (for)="candy of favourites"><div>${json(candy)}</div></div>

Output:

{"snickers":true}
{"milky way":false}

(hide) - Show element conditionnally

Example 1:

<p (hide)="2 + 2 === 5">Wow, 2 + 2 is 5</p>
<p (hide)="2 + 2 === 4">As always, 2 + 2 is 4</p>
<p (init)="truth = true" (hide)="truth">Everything is truth</p>
<p (init)="lie = false" (hide)="!lie">Lie</p>

(hide) sets element's hidden attribute if expression is true. Example 2:

<p (hide)="2 + 2 === 4">2 + 2 is 4, so that element will be hidden</p>

You can use ! to hide element if value is false. Also you can bind directive to variable:

<p (init)="truth = true" (hide)="truth">Everything is truth</p>
<p (init)="lie = true" (hide)="!lie">Lie</p>

Advanced

Eval {{expression}} syntax

<p>This expression is precomputed: {{2 + 2}}</p>
<p (init)="x = 4 + 4">This expression is binded to "x" variable: {{x}}</p>

Xyp defines and uses

$data
$routes
$subscribes
$directives
$routeParameters

global variables. Make sure that your code is not overwriting them. Example:

<h1 (init)="title = 'Add more A: '">{{title}}</h1>
<button onclick="$data['title'] += 'A'">Add A here</button>

In this example $data['title'] is initialized with value 'Add more A: ' and then button click adds 'A' to the var.

Reactiveness

Xyp reactiveness mechanism is quite simple: item is initialized in $data variable with setter invoking all functions in corresponding $subscribes object. To subscribe on change of some variable in $data, push callback in $subscribes or use subscribe function. Example:

<!-- app-root.html -->
<h1 (init)="title = 'hello!'" (listen)="title"></h1>

component.ts:

/// <reference path="../../xypts/init.ts"/>
/// <reference path="../../xypts/component.ts"/>
@Component('app-root')
class AppRootComponent extends XypComponent {
  constructor() {
    super('app-root');
    setTimeout(() => {
      $data['title'] = 'hi there!';
    }, 500);
    subscribe('title', () => console.log('title has changed!'));
  }
}

Routing

Xyp support routing. That means that Xyp can be used to create SPA(single-page application)s. Add this component where routing is needed:

<xyp-router></xyp-router>

Create some page components and prepend @Route decorator in component.ts. Example:

app-root.html

<a href="#/test">Test</a>
<xyp-router></xyp-router>

In new component called 'test-page':

component.ts:

/// <reference path="../../xypts/component.ts"/>
/// <reference path="../../xypts/routes.ts"/>
@Route({
  selector: 'test-page',
  route: '/test',
})
@Component('test-page')
class TestPageComponent extends XypComponent {
  constructor() {
    super('test-page');
  }
}

If URL ends with #/test, there should be test-page works! text. Also you can pass parameters to the route like:

http://localhost:9000/#/test?foo=bar

Now, parameter will be available in $routeParameters['foo'], e.g $routeParameters['foo'] is 'bar'.

Functions

$http - send asynchronous HTTP request
interface HTTPRequest {
  headers?: { [key: string]: string };

  url: string;
  method: string;

  body?: any;
  urlEncodeBody?: boolean;

  parseJSON?: boolean;
}

function $http<T>(requestData: HTTPRequest): Promise<T>

$http sends HTTP request and returns promise with response body. If urlEncodeBody is true, request body is encoded as URL. If parseJSON is set, response body is parsed as JSON and returned in Promise as Object, otherwise string casted to T is returned.

listen - subscribe for object's property's changes
function listen(object: any, objname: string, callback: Function)

listen tries to inject setter and getter of object's objname proprety with defined callback. This function must NOT be called with $data object. Use bind function instead.

bind - inject reactive setter and getter of $data[objname]
function bind(objname: string, callback?: ReactiveListener)

Bind calls listen function and adds callback invoked on $data[objname]'s change, if present.

ReactiveListener

ReactiveListener has two fields: isDeletable(checks whether this subscribe will update DOM), and callback(subscribe). Use DefaultReactiveListener function to create ReactiveListener.

function DefaultReactiveListener(el: HTMLElement, callback: Function)

Comparation to other frameworks

Counter app

Xyp

<h1 (init)="count = 0">{{count}}</h1>
<button onclick="$data['count']--">-</button>
<button onclick="$data['count']++">+</button>

React

import React from "react";
import ReactDOM from "react-dom";

class Counter extends React.Component {
    constructor(props) {
        super(props);
        this.state = { count: 0};
    }

    down(value) {
        this.setState(state => ({ count: state.count - value }));
    }
    up(value) {
        this.setState(state => ({ count: state.count + value }));
    }

    render() {
        return (
            <div>
                <h1>{this.state.count}</h1>
                <button onClick = {() => this.down(1)}>-</button>
                <button onClick = {() => this.up(1)}>+</button>
            </div>
        );
    }
}
ReactDOM.render(<Counter />, document.querySelector("#app"));

Vue

import Vue from "vue";

new Vue({
    data: { count: 0 },

    methods: {
        down: function(value) {
            this.count -= value;
        },
        up: function(value) {
            this.count += value;
        }
    },

    render: function(h) {
        return(
            <div>
                <h1>{this.count}</h1>
                <button onClick={() => this.down(1)}>-</button>
                <button onClick={() => this.up(1)}>+</button>
            </div>
        );
    },

    el: "#app"
});

Hyperapp

import { h, app } from "hyperapp";

const state = {
    count: 0
};

const actions = {
    down: value => state => ({ count: state.count - value}),
    up: value => state => ({ count: state.count + value})
};

const view = (state, actions) => (
    <div>
        <h1>{state.count}</h1>
        <button onclick={() => actions.down(1)}>-</button>
        <button onclick={() => actions.up(1)}>+</button>
    </div>
);

app(state, actions, view, document.querySelector("#app"));

Svelte

<div>
  <h1>{count}</h1>
  <button on:click="set({count: count - 1})">-</button>
  <button on:click="set({count: count + 1})">+</button>
</div>

Asynchronous app

Xyp

app-root.html

<button onclick="getPosts()">Get posts</button>
<div (for)="post of posts">
    <div id="${post.id}">
        <h2><font color="#3AC1EF">${post.title}</font></h2>
        <p>${post.body}</p>
    </div>
</div>

component.ts

/// <reference path="../../xypts/init.ts"/>
/// <reference path="../../xypts/component.ts"/>
@Component('app-root')
class AppRootComponent extends XypComponent {
  constructor() {
    super('app-root');
    $data['posts'] = [];
  }
}

interface Post {
  id: number;
  userId: number;
  title: string;
  body: string;
}
function getPosts() {
  $http<Post[]>({ url: 'https://jsonplaceholder.typicode.com/posts', method: 'GET', parseJSON: true }).then(data => {
    $data['posts'] = data;
  });
}

React

import React from "react";
import ReactDOM from "react-dom";

class PostViewer extends React.Component {
    constructor(props) {
        super(props);
        this.state = { posts: [] };
    }

    getData() {
        fetch(`https://jsonplaceholder.typicode.com/posts`)
        .then(response => response.json())
        .then(json => {
            this.setState(state => ({ posts: json}));
        });
    }

    render() {
        return (
            <div>
                <button onClick={() => this.getData()}>Get posts</button>
                {this.state.posts.map(post => (
                    <div key={post.id}>
                        <h2><font color="#3AC1EF">{post.title}</font></h2>
                        <p>{post.body}</p>
                    </div>
                ))}
            </div>
        );
    }
}

ReactDOM.render(<PostViewer />, document.querySelector("#app"));

Vue

import Vue from "vue";

new Vue({
    data: { posts: [] },

    methods: {
        getData: function(value) {
            fetch(`https://jsonplaceholder.typicode.com/posts`)
            .then(response => response.json())
            .then(json => {
                this.posts = json;
            });
        }
    },

    render: function(h) {
        return (
            <div>
                <button onClick={() => this.getData()}>Get posts</button>
                {this.posts.map(post => (
                    <div key={post.id}>
                        <h2><font color="#3AC1EF">{post.title}</font></h2>
                        <p>{post.body}</p>
                    </div>
                ))}
            </div>
        );
    },

    el: "#app"
});

Hyperapp

import { h, app } from "hyperapp";

const state = {
    posts: []
};

const actions = {
    getData: () => (state, actions) => {
        fetch(`https://jsonplaceholder.typicode.com/posts`)
        .then(response => response.json())
        .then(json => {
            actions.getDataComplete(json);
        });
    },
    getDataComplete: data => state => ({ posts: data })
};

const view = (state, actions) => (
    <div>
        <button onclick={() => actions.getData()}>Get posts</button>
        {state.posts.map(post => (
            <div key={post.id}>
                <h2><font color="#3AC1EF">{post.title}</font></h2>
                <p>{post.body}</p>
            </div>
        ))}
    </div>
);

app(state, actions, view, document.querySelector("#app"));

Svelte

<div>
  <button on:click="getData()">Get posts</button>
  {#each posts as {title, body}}
  <div>
    <h2><font color="#3AC1EF">{title}</font></h2>
    <p>{body}</p>
  </div>
  {/each}
</div>

<script>
  export default {
    methods: {
      getData() {
        fetch('https://jsonplaceholder.typicode.com/posts')
          .then(res => res.json())
          .then(posts => this.set({ posts }));
      }
    }
  };
</script>