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

exsense-editor

v0.0.2

Published

A Monaco-based expression editor with intellisense and syntax highlighting

Readme

ExSense Expression Editor

A Monaco-based expression editor with intellisense, syntax highlighting, and validation for a custom expression language.

Features

  • Syntax highlighting based on the Expresso language syntax
  • IntelliSense with autocompletion for variables, functions, operators
  • Dynamic context variables support with nested object property suggestions
  • Hover documentation for functions and operators
  • Real-time validation
  • Custom themes and styling
  • Based on Monaco Editor (same as VS Code)

Installation

NPM

npm install exsense-editor monaco-editor

Yarn

yarn add exsense-editor monaco-editor

Basic Usage

HTML and JavaScript

<!DOCTYPE html>
<html>
<head>
  <title>ExSense Editor</title>
  <!-- Include the ExSense CSS -->
  <link rel="stylesheet" href="node_modules/exsense-editor/dist/exsense.css">
  <style>
    #editor-container {
      width: 800px;
      height: 300px;
      margin: 20px auto;
    }
    #error-container {
      width: 800px;
      margin: 10px auto;
      color: red;
    }
  </style>
</head>
<body>
  <div id="editor-container"></div>
  <div id="error-container"></div>

  <!-- Include Monaco Editor first -->
  <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
  <!-- Then include ExSense -->
  <script src="node_modules/exsense-editor/dist/exsense.min.js"></script>
  
  <script>
    // Configure loader for Monaco Editor
    require.config({ paths: { 'vs': 'node_modules/monaco-editor/min/vs' }});
    
    // After Monaco is loaded, initialize ExSense
    require(['vs/editor/editor.main'], function() {
      // Create an instance of ExSense
      const exsense = new ExSense(
        'editor-container',
        'error-container',
        'node_modules/exsense-editor/dist/syntax-rules.json',
        {
          user: {
            name: "John Doe",
            age: 32,
            roles: ["admin", "editor"]
          },
          settings: {
            currency: "USD",
            taxRate: 0.08
          }
        }
      );
      
      // Initialize the editor
      exsense.initialize()
        .then(() => {
          // Set initial expression
          exsense.setValue('$user.age > 18 && contains($user.roles, "admin")');
          
          // Subscribe to validation changes
          exsense.onValidationChange((markers, severity) => {
            const errorCount = markers.filter(m => m.severity === severity.Error).length;
            console.log(`Validation: ${errorCount} errors`);
          });
          
          // Subscribe to content changes
          exsense.onContentChange((content) => {
            console.log('Expression changed:', content);
          });
          
          // Subscribe to action button clicks
          exsense.onAction((data) => {
            console.log('Action triggered with expression:', data.expression);
          });
        });
    });
  </script>
</body>
</html>

ES Modules

import { ExSense } from 'exsense-editor';
import 'exsense-editor/dist/exsense.css';

// You need to ensure Monaco Editor is loaded before initializing ExSense
// This can be done using Monaco's loader or a package like monaco-editor-webpack-plugin

const initEditor = async () => {
  const exsense = new ExSense(
    'editor-container',
    'error-container',
    '/path/to/syntax-rules.json',
    { 
      user: { name: 'John', age: 25 } 
    }
  );
  
  await exsense.initialize();
  exsense.setValue('$user.age >= 18');
  
  // Subscribe to events
  exsense.onContentChange(content => {
    console.log('Content changed:', content);
  });
};

initEditor();

Angular Integration

Installation

First, install both ExSense and Monaco Editor:

npm install exsense-editor monaco-editor

Step 1: Configure Monaco Editor

In your angular.json, add Monaco Editor assets:

{
  "projects": {
    "your-project": {
      "architect": {
        "build": {
          "options": {
            "assets": [
              "src/favicon.ico",
              "src/assets",
              {
                "glob": "**/*",
                "input": "node_modules/monaco-editor/min",
                "output": "/assets/monaco/"
              },
              {
                "glob": "syntax-rules.json",
                "input": "node_modules/exsense-editor/dist",
                "output": "/assets/"
              }
            ],
            "styles": [
              "src/styles.css",
              "node_modules/exsense-editor/dist/exsense.css"
            ]
          }
        }
      }
    }
  }
}

Step 2: Create an ExSense Component

// exsense.component.ts
import { Component, OnInit, OnDestroy, Input, Output, EventEmitter, ElementRef } from '@angular/core';
import { ExSense } from 'exsense-editor';

@Component({
  selector: 'app-exsense',
  template: `
    <div class="exsense-container">
      <div [id]="editorId" class="monaco-editor-container"></div>
      <div [id]="errorId" class="error-container"></div>
    </div>
  `,
  styles: [`
    .exsense-container {
      width: 100%;
      height: 300px;
    }
    .monaco-editor-container {
      width: 100%;
      height: 280px;
    }
    .error-container {
      width: 100%;
      min-height: 20px;
      color: red;
    }
  `]
})
export class ExSenseComponent implements OnInit, OnDestroy {
  @Input() value: string = '';
  @Input() context: Record<string, any> = {};
  
  @Output() valueChange = new EventEmitter<string>();
  @Output() validationChange = new EventEmitter<any>();
  @Output() action = new EventEmitter<any>();
  
  private editorId = `editor-${Math.random().toString(36).substr(2, 9)}`;
  private errorId = `error-${Math.random().toString(36).substr(2, 9)}`;
  private editor: any;
  
  constructor(private el: ElementRef) {}
  
  ngOnInit() {
    // Load Monaco Editor
    const onGotAmdLoader = () => {
      // Load monaco
      (<any>window).require.config({ paths: { 'vs': 'assets/monaco/vs' } });
      (<any>window).require(['vs/editor/editor.main'], () => {
        this.initializeExSense();
      });
    };

    // Load AMD loader if necessary
    if (!(<any>window).require) {
      const loaderScript = document.createElement('script');
      loaderScript.type = 'text/javascript';
      loaderScript.src = 'assets/monaco/vs/loader.js';
      loaderScript.addEventListener('load', onGotAmdLoader);
      document.body.appendChild(loaderScript);
    } else {
      onGotAmdLoader();
    }
  }
  
  ngOnDestroy() {
    if (this.editor) {
      this.editor.dispose();
    }
  }
  
  private async initializeExSense() {
    this.editor = new ExSense(
      this.editorId,
      this.errorId,
      'assets/syntax-rules.json',
      this.context
    );
    
    await this.editor.initialize();
    
    if (this.value) {
      this.editor.setValue(this.value);
    }
    
    this.editor.onContentChange((content: string) => {
      this.valueChange.emit(content);
    });
    
    this.editor.onValidationChange((markers: any[], severity: any) => {
      this.validationChange.emit({ markers, severity });
    });
    
    this.editor.onAction((data: any) => {
      this.action.emit(data);
    });
  }
  
  // Public API
  public setValue(value: string) {
    if (this.editor) {
      this.editor.setValue(value);
    }
  }
  
  public getValue(): string {
    return this.editor ? this.editor.getValue() : '';
  }
  
  public setContextVariables(context: Record<string, any>) {
    if (this.editor) {
      this.editor.setContextVariables(context);
    }
  }
}

Step 3: Add the Component to a Module

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { ExSenseComponent } from './exsense.component';

@NgModule({
  declarations: [
    AppComponent,
    ExSenseComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Step 4: Use the Component

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <div>
      <h1>ExSense Editor</h1>
      <app-exsense 
        [value]="expression" 
        [context]="contextVariables"
        (valueChange)="onExpressionChange($event)"
        (validationChange)="onValidationChange($event)"
        (action)="onAction($event)"
      ></app-exsense>
      <div *ngIf="error" class="error">{{ error }}</div>
      <div *ngIf="result" class="result">{{ result }}</div>
    </div>
  `,
  styles: [`
    .error { color: red; margin: 10px 0; }
    .result { color: green; margin: 10px 0; }
  `]
})
export class AppComponent {
  expression = '$user.age > 21 && contains($user.roles, "admin")';
  
  contextVariables = {
    user: {
      name: 'Alice',
      age: 30,
      roles: ['admin', 'user'],
      preferences: {
        theme: 'dark'
      }
    }
  };
  
  error: string | null = null;
  result: string | null = null;
  
  onExpressionChange(expression: string) {
    console.log('Expression changed:', expression);
  }
  
  onValidationChange(event: any) {
    const errorCount = event.markers.filter((m: any) => m.severity === event.severity.Error).length;
    this.error = errorCount > 0 ? `Expression has ${errorCount} errors` : null;
  }
  
  onAction(data: any) {
    this.result = `Evaluated: "${data.expression}"`;
    // In a real app, you would evaluate the expression here
  }
}

Docusaurus Integration

ExSense can be integrated with Docusaurus documentation sites to provide interactive examples.

Installation in Docusaurus

npm install exsense-editor monaco-editor

Basic Integration

  1. Create a React component in your Docusaurus project:
// src/components/ExSenseEditor.jsx
import React, { useEffect, useRef, useState } from 'react';
import BrowserOnly from '@docusaurus/BrowserOnly';
import useIsBrowser from '@docusaurus/useIsBrowser';
import { ExSense } from 'exsense-editor';
import 'exsense-editor/dist/exsense.css';

export default function ExSenseEditor({ value, context, height }) {
  const isBrowser = useIsBrowser();
  
  if (!isBrowser) {
    return <div>ExSense Editor (requires browser JavaScript)</div>;
  }
  
  return (
    <BrowserOnly>
      {() => {
        // Implementation that loads ExSense in the browser
        // See full documentation for details
      }}
    </BrowserOnly>
  );
}
  1. Use it in your MDX documentation:
---
title: Expression Editor Examples
---

import ExSenseEditor from '@site/src/components/ExSenseEditor';

# Expression Editor Examples

<ExSenseEditor 
  value="$user.age > 18" 
  context={{ user: { name: "John", age: 30 } }}
  height="250px"
/>

For detailed Docusaurus integration instructions, see the files in this repository:

Component API

// Create a new instance
const exsense = new ExSense(
  'editor-container-id',         // DOM Element ID for editor
  'error-container-id',          // DOM Element ID for error messages
  'path/to/syntax-rules.json',   // Path to syntax rules file
  contextVariables,              // Optional object with context variables
  config                         // Optional configuration object
);

// Initialize the editor
await exsense.initialize();

// Get the current expression
const value = exsense.getValue();

// Set an expression
exsense.setValue('$user.age > 18');

// Update context variables dynamically
exsense.setContextVariables({
  user: {
    name: 'John',
    age: 30,
    roles: ['admin', 'user']
  }
});

// Subscribe to events (new subscription pattern)
const actionSubscription = exsense.onAction((data) => {
  console.log('Action triggered:', data.expression);
});

// Unsubscribe when done
actionSubscription.unsubscribe();

// Dispose when done
exsense.dispose();

Configuration Options

const config = {
  // Show/hide toolbar
  showToolbar: true,
  
  // Configure which toolbar buttons to show
  toolbarButtons: {
    editContext: true,
    examples: true,
    action: true
  },
  
  // List of example expressions
  examplesList: [
    { name: 'Simple Condition', expression: '$user.age > 18' },
    { name: 'Complex Logic', expression: '$user.isActive && contains($user.roles, "admin")' }
  ],
  
  // Context button configuration
  contextButton: {
    label: 'Edit Context',
    icon: '<svg>...</svg>', // SVG icon string
    style: {
      backgroundColor: '#f0f0f0',
      color: '#333'
    }
  },
  
  // Examples button configuration
  examplesButton: {
    label: 'Examples',
    icon: '📋',
    emptyText: 'No examples available',
    style: {
      backgroundColor: '#f0f0f0'
    }
  },
  
  // Action button configuration
  actionButton: {
    label: 'Evaluate',
    icon: '<svg>...</svg>', // SVG icon string
    style: {
      backgroundColor: '#4CAF50',
      color: 'white'
    }
  }
};

License

MIT