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

ember-repl

v5.0.1

Published

Addon for enabling REPL and Playground creation with Ember/Glimmer

Downloads

7,260

Readme

ember-repl

npm version CI

Tools for easily creating your own Ember Playground / REPL and/or Interactive StyleGuide for your design system.

This package will include all available dev-time dependencies provided by ember + glimmer as well as @babel/standalone. Your payload will be affected and Embroider is recommended with maximum strictness enabled so automatic bundle splitting occurs to help your app's initial time-to-interactive/etc stats.

Compatibility

  • Ember.js v3.27 or above
  • Ember CLI v3.27 or above
  • Webpack v5 or above
  • ember-auto-import v2 or above
  • Node.js v14 or above

Capabilities

  • gjs
  • hbs
  • markdown with gjs/hbs blocks (glimdown)

Installation

ember install ember-repl

Setup

This library uses babel, which does some goofy things in the browser. You'll need to define a global process and Buffer somewhere in your app.

For example:

// app/app.js

// @babel/traverse (from babel-plugin-ember-template-imports)
// accesses process.....
// maybe one day we can have a browser-only verison?
// But they aren't used.... so.. that's fun.
Object.assign(window, {
  process: { env: {} },
  Buffer: {},
});

Usage

Compiling GJS

There are two ways to compile gjs text, imperatively via compileJS, where you manage the reactivity yourself. Or Compiled, which is a resource that manages the reactivity for you.

Automatic reactivity via the Resource

Following the Resources documentation, you can use Compiled in both template-only or class-backed contexts:

import { Compiled } from 'ember-repl';

<template>
  {{#let (Compiled @gjsText 'gjs') as |compileResult|}}

    {{#if compileResult.error}}
      an error! {{compileResult.error}}
    {{/if}}

    {{#if compileResult.component}}
      <compileResult.component />
    {{/if}}

  {{/let}}
</template>

One advantage of using a backing JS context, is that you can utilize the keepLatest resource so that when an error occurs, you could keep rendering the latest successful compile.

import Component from '@glimmer/component';
import { Compiled } from 'ember-repl';
import { use } from 'ember-resources'; 
import { keepLatest } from 'reactiveweb/keep-latest';

export class Renderer extends Component {
  @use compile = Compiled(() => this.args.gjsText, 'gjs');

  @use latest = keepLatest({
    value: () => this.compile.component,
    when: () => this.compile.error,
  }); 

  <template> 
    {{#if this.compile.error}}
      Error! {{this.compile.error}}
    {{/if}}

    {{! This will keep showing even when there is an error.
        Which can help reduce visual jitter }} 
    {{#if this.latest.value}}
      <this.latest.latest />
    {{/if}}

  </template>
}

Managing your own reactivity

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { compileJS } from 'ember-repl';

export class Renderer extends Component {
  @tracked compileResult;

  constructor(...args) {
    super(...args);

    compileJS('...').then((compileResult) => this.compileResult = compileResult);
  }
}
{{#if this.compileResult.component}}
  <this.compileResult.component />
{{/if}}

Compiling HBS

Automatic reactivity via the Resource

The hbs utilities do not provide a utility Resource.

Managing your own reactivity

import Component from '@glimmer/component';
import { compileHBS } from 'ember-repl';

export class Renderer extends Component {
  compileResult = compileHBS(this.args.input);
}
<this.compileResult.component />

Compiling Markdown

There are two ways to compile markdown text, imperatively via compile (passing the glimdown format), where you manage the reactivity yourself. Or CompileMarkdown, which is a resource that manages the reactivity for you.

Automatic reactivity via the Resource

Following the Resources documentation, you can use Compiled in both template-only or class-backed contexts:

import { Compiled } from 'ember-repl';

<template>
  {{#let (Compiled @mdText 'glimdown') as |compileResult|}}

    {{#if compileResult.error}}
      an error! {{compileResult.error}}
    {{/if}}

    {{#if compileResult.component}}
      <compileResult.component />
    {{/if}}

  {{/let}}
</template>

One advantage of using a backing JS context, is that you can utilize the keepLatest resource so that when an error occurs, you could keep rendering the latest successful compile.

import Component from '@glimmer/component';
import { Compiled } from 'ember-repl';
import { use } from 'ember-resources'; 
import { keepLatest } from 'reactiveweb/util/keep-latest';

export class Renderer extends Component {
  @use compile = Compiled(() => this.args.mdText, 'glimdown');

  @use latest = keepLatest({
    value: () => this.compile.component,
    when: () => this.compile.error,
  }); 

  <template> 
    {{#if this.compile.error}}
      Error! {{this.compile.error}}
    {{/if}}

    {{! This will keep showing even when there is an error.
        Which can help reduce visual jitter }} 
    {{#if this.latest.value}}
      <this.latest.latest />
    {{/if}}

  </template>
}

Managing your own reactivity

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { compile } from 'ember-repl';

export class Renderer extends Component {
  @tracked component;
  @tracked error;
  @tracked isCompiling;

  constructor(...args) {
    super(...args);

    compile('...', {
      format: 'glimdown', // or 'gjs' or 'hbs'
      onSuccess: async (component) => {
        this.error = null;
        this.isCompiling = false; 
        this.component = component;
        
      },
      onError: async (error) => {
        this.isCompiling = false; 
        this.error = error;
      },
      onCompileStart: async () => {
        this.isCompiling = true; 
      }
    });
  }
}
{{#if this.component}}
  <this.component />
{{/if}}

Using existing components

ember-repl is strict-mode only, so any component that you want to invoke needs to be passed to the scope option of compileHBS or compileJS. Following code is assuming that right next to our Renderer component there is a component named Bar.

import Component from '@glimmer/component';
import { compileHBS } from 'ember-repl';
import BarComponent from './bar'

export class Renderer extends Component {
  compileResult = compileHBS(
    '<Bar />',
    {
      scope: {
        Bar: BarComponent
      }
    }
  );
}

Modifiers and Helpers

When writing components / demos / examples using this library, you must use template-strict mode. Strict mode isn't available by default in proper ember apps yet. The main difference in strict mode is that all globals must be imported.

Example of a template-only component that only provides a button:

import { on } from '@ember/modifier';
import { fn, hash } from '@ember/helper';

<template>
  <button {{on 'click' (fn @callback (hash a=1 b=2))}}>...</button>
</template>

For a list of all the imports for things that are global in loose mode, view the Strict Mode RFC

Expecting Errors

compileJS and compileHBS may result an an error.

To handle this, you'll want to make sure that rendering the component output is guarded by either:

  • the truthiness of component (which is undefined if error is present)
  • the falsiness of error (which is undefined if compilation was successful)

Depending on your desired UI/UX, how the async build of updates to input is conveyed may vary and is not provided by this library. Here is an example of a way that someone could handle rendering with compileJS:

export default class AwaitBuild extends Component {
  @tracked component;
  @tracked error;

  constructor(...args) {
    super(...args);

    compileJS(args.inputText)
      .then(({ component, error }) => {
        this.component = component;
        this.error = error;
      })
      .catch(error => this.error = error);
  }

  get isPending() {
    return !this.component';
  }
}
{{#if this.error}}
  Error: {{this.error}}
{{else if this.isPending}}
  Building...
{{else}}
  <this.component />
{{/if}}

A Note on Capabilities This library currently uses a CommonJS technique for modules, but as browser-support permits, this library will eventually switch to using a web-worker with an import-map for lightning fast, eval-free REPLing. (But the same security caution below would still apply)

API

Methods

  • compileJS: async, returns compileResult - compiles a single JS file uses the syntax from ember-template-imports
  • compileHBS: returns compileResult - compiles a template-only component with no dependencies
  • invocationOf: string - converts hyphenated text to an <AngleBracketInvocation />
  • nameFor: string - generates a component-safe GUID-like derivation from code

Properties

interface CompileResult {
  // invokable from templates
  component?: unknown;

  // if there is a compilation error, this will be non-falsey
  error?: Error;

  // the name assigned to the input text via UUIDv5
  name: string;
}

Using in an app that uses Embroider

If you are using the Webpack packager, you will need these settings:

packagerOptions: {
  webpackConfig: {
    node: {
      global: false,
      __filename: true,
      __dirname: true,
    },
    resolve: {
      fallback: {
        path: 'path-browserify',
      },
    },
  },
},

Security

Many developers know that evaluating runnable user input is a huge security risk. To mitigate risk, this library should not be used in an environment that has access to sensitive data. Additionally, end-users of this library (users of the consuming app) should be made aware of the risk so that they themselves do not paste foreign / unrecognized / untrusted code into the REPL.

This library itself will stay as up to date as possible, and if there are any security concerns, please email security [at] nullvoxpopuli.com

Contributing

See the Contributing guide for details.

License

This project is licensed under the MIT License.