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

riot-query

v1.0.0

Published

Lets you traverse through a tree of Riot tags and underlying DOM elements

Downloads

4

Readme

riot-query

A module for easily traversing through a Riot.js tag hierarchy, using a simple custom query language. Can be used stand-alone or as a tag mixin.

Why you might need this

  • Riot.js is inconsistent in how it exposes its tags - it will be an array if there are multiple, and a single element if there's only one. This means you constantly have to normalize the results.
  • While selecting direct descendants is easy, it's a lot trickier when you need to search in the entire hierarchy, for example because you have a wrapper element that yields its content.

License

WTFPL or CC0, whichever you prefer. A donation and/or attribution are appreciated, but not required.

Donate

Maintaining open-source projects takes a lot of time, and the more donations I receive, the more time I can dedicate to open-source. If this module is useful to you, consider making a donation!

You can donate using Bitcoin, PayPal, Flattr, cash-in-mail, SEPA transfers, and pretty much anything else. Thank you!

Contributing

Pull requests welcome. Please make sure your modifications are in line with the overall code style, and ensure that you're editing the files in src/, not those in lib/.

Build tool of choice is gulp; simply run gulp while developing, and it will watch for changes.

Be aware that by making a pull request, you agree to release your modifications under the licenses stated above.

Query language

A query consists of two parts: a Riot query and a DOM query. Both are optional, but you must always specify at least one of them.

In the Riot part of a query, the structure always refers to the hierarchy of Riot tags, not other DOM elements. In other words, the hierarchy follows the one you'd find in this.tags. In the DOM part of the query, you can specify DOM elements to select in the set of Riot tags that you've selected.

The riot-query language is somewhat syntactically similar to shell expansion. Below is a reference chart:

| Syntax | Meaning | |-----------------------------|---------------------------------------------------| | foo | Each foo tag. | | foo/bar | Each bar tag that is a direct descendant of a foo tag. | | foo/**/bar | Each bar tag that is a child of a foo tag, anywhere in its subtree - it does not have to be a direct descendant. | | {foo,bar} | Each foo and bar tag | | foo/{bar,baz} | Each bar and baz tag that is a direct descendant of a foo tag. | | foo/{bar,baz,} | Each foo, foo/bar and foo/baz tag (note the last comma!) | | foo//.someClass | Each DOM element matching .someClass that exists somewhere within a foo tag. | | foo//.someClass > .sub | Each DOM element matching .someClass > .sub that exists somewhere within a foo tag. |

These are only some simplified examples - any combination of the above is valid, as long as the (optional) DOM part of the query comes last. riot-query uses a real query parser, so there are no edge cases.

Usage

An example of a simple query:

menu-bar/{menu-button,menu-submenu}

This would select every menu-button and menu-submenu that is directly nested within a menu-bar.

An example of a contrived, complex query:

foo/bar/**/{baz,qux/quz,something/{hai,bai},}/{duck,swan}//.domFoo .domBar

This would select every DOM element matching .domFoo .domBar that exists within any of the following paths:

  • foo/bar/**/baz/duck
  • foo/bar/**/baz/swan
  • foo/bar/**/qux/quz/duck
  • foo/bar/**/qux/quz/swan
  • foo/bar/**/something/hai/duck
  • foo/bar/**/something/hai/swan
  • foo/bar/**/something/bai/duck
  • foo/bar/**/something/bai/swan
  • foo/bar/**/duck
  • foo/bar/**/swan

The last two might seem a little odd, but note the extra comma at the end of the first group - a trailing comma is considered to indicate an empty item, similar to how it works in Bash, and in riot-query that means that the entire segment is ignored.

An example of making a query using riot-query as a mixin:

<menu-bar>
	<menu-section>
		<menu-item>Text 1</menu-item>
		<menu-item>Text 2</menu-item>
	</menu-section>
	
	<script>
		this.mixin(require("riot-query").mixin);
		
		this.on("mount", () => {
			this.query("**/menu-item").forEach((menuItem) => {
				menuItem.on("clicked", () => {
					console.log("Clicked menu item", menuItem);
				});
			});
		});
	</script>
</menu-bar>

A similar example, but selecting DOM nodes rather than Riot tags:

<menu-bar>
	<menu-section>
		<menu-item>
			<i class="icon icon-house"></i>
			Text 1
		</menu-item>
		<menu-item>
			<i class="icon icon-keys"></i>
			Text 2
		</menu-item>
	</menu-section>
	
	<script>
		this.mixin(require("riot-query").mixin);
		
		this.on("mount", () => {
			this.query("**/menu-item//i.icon").forEach((icon) => {
				console.log("Icon classes", icon.classList);
			});
		});
	</script>
</menu-bar>

An example using riot-query stand-alone rather than as a mixin:

<menu-bar>
	<menu-section>
		<menu-item>
			<i class="icon icon-house"></i>
			Text 1
		</menu-item>
		<menu-item>
			<i class="icon icon-keys"></i>
			Text 2
		</menu-item>
	</menu-section>
	
	<script>
		const riotQuery = require("riot-query");
		
		this.on("mount", () => {
			riotQuery(this, "**/menu-item//i.icon").forEach((icon) => {
				console.log("Icon classes", icon.classList);
			});
		});
	</script>
</menu-bar>

API

riotQuery(tag, query)

Run a query on a given tag, and return the results.

  • tag: The Riot.js tag to query from. This cannot be a regular DOM element, it must be a tag.
  • query: The query to run, following the query syntax described above.

If the query contains a DOM query, this function will return DOM elements. If it doesn't, it will return Riot tags. If only a DOM query is specified without a Riot query (eg. //.someClass), then riot-query will search through the DOM tree for the tag that riotQuery was called on.

Keep in mind that if your query selects DOM nodes rather than Riot tags, they will be returned as an array, not as a NodeList. Additionally, DOM queries will also include DOM elements in child tags, not just those that are a part of the tag you're querying from.

riotQuery.one(tag, query)

The same as riotQuery, but it only ever returns the first result, without being wrapped in an array.

riotQuery.mixin

A mixin object that you can pass into the this.mixin method for any Riot tag.

Mixin API

this.query(query)

The same as riotQuery, but without the need to specify a tag - it will always apply to the current tag.

this.queryOne(query)

The same as riotQuery.one, but without the need to specify a tag.