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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lune-js/astro

v1.0.0

Published

Use Lune within Astro.

Readme

This Astro integration adds Lune.js to your project so that you can use Lune.js anywhere on your page.

Installation

npm install @lune-js/astro lune-js

Register the integration in your Astro config:

// astro.config.mjs
import { defineConfig } from "astro/config";
import lune from "@lune-js/astro";

export default defineConfig({
  integrations: [lune()]
});

Usage

Write Lune directives directly in your markup — no <script> tag and no createApp() call. The integration boots a single app per page and mounts it to every top-level lu-scope.

---
// src/pages/index.astro
---

<html>
  <head>
    <title>Lune 🌙 x Astro 🚀</title>
  </head>
  <body>
    <div lu-scope="{ count: 1 }">
      <p lu-text="count"></p>
      <button @click="count++">increment</button>
      <button @click="count--">decrement</button>
    </div>
  </body>
</html>

[!NOTE] Astro's template syntax claims single braces, so {{ }} interpolation does not work inside .astro files. Use lu-text, or set custom $delimiters that Astro leaves alone.

Options

entrypoint

Point entrypoint at a module that configures Lune before it mounts.

// astro.config.mjs
export default defineConfig({
  integrations: [lune({ entrypoint: "/src/entrypoint" })]
});

The path may be a root-relative import specifier (/src/entrypoint) or relative to your project root (./src/entrypoint). The module may provide either or both of the exports below; if it provides neither, the integration warns in development and mounts an unmodified app.

data

A data export is passed straight to createApp(), becoming the app's root scope. Every lu-scope on the page inherits from it, which makes it the place for shared scope factories and for options Lune reads at creation time, such as $delimiters.

// src/entrypoint.ts
export const data = {
  $delimiters: ["[[", "]]"],
  Counter: (start = 0) => ({
    count: start,
    get double() {
      return this.count * 2;
    },
    increment() {
      this.count++;
    }
  })
};
<div lu-scope="Counter(5)">
  <p>[[ count ]]</p>
  <p lu-text="double"></p>
  <button @click="increment">increment</button>
</div>

Because this object is a real module export rather than serialized config, it can hold methods and getters — not just JSON. Custom $delimiters are worth calling out: they are read only at creation time, so this export is the only way to set them, and picking delimiters Astro ignores makes interpolation usable inside .astro files.

default

The default export receives the app instance after it is created but before it mounts. Use it to register custom directives and install plugins.

// src/entrypoint.ts
import type { App } from "lune-js";

export default (app: App) => {
  app.directive("focus", ({ el }) => el.focus());
};

window.Lune

The integration exposes window.Lune as an escape hatch for inline scripts, carrying reactive, effect, nextTick, and createApp.

window.Lune.createApp() is not Lune's bare createApp — it is the integration's, so any app you create from it gets the same data and customizations as the one the integration mounts for you. Anything you pass takes precedence over the entrypoint's data:

window.Lune.createApp({ user }).mount(el);

That matters with Astro's <ClientRouter />. The integration mounts on DOMContentLoaded, which does not fire on client-side navigations, so re-mount on astro:after-swap and the new app keeps your entrypoint:

<script>
  if (!window.__luneRemount) {
    window.__luneRemount = true;
    document.addEventListener("astro:after-swap", () => window.Lune.createApp().mount());
  }
</script>

The guard matters: listeners on document survive swaps, so registering unconditionally would stack a listener on every navigation.

Documentation

Full guide: lune-js.com/integrations/astro

License

MIT