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

@satelite/engineer

v3.4.0

Published

Data-driven code generation tool

Downloads

13

Readme

Contributors Forks Stargazers Issues

Satelite Engineer

“Any sufficiently advanced technology is indistinguishable from magic”

— Arthur C. Clarke

What is it?

Think Gatsby but language agnostic, much simpler since its only the code generation engine, and while it can also be used as a SSG (Static Site Generator) this is not its main focus or target use. It is highly extendible and programmable, it can be integrated into modern CI/CD toolchains and can be connected to any data source, or many, via plugins.

In short, it is a tool that will take template files that you write, and a data structure you provide, to generate new files using the provided data.

How does it work?

First you need to have these installed on your device

Then you can install the CLI

Install Satelite Engineer CLI:

npm i -g @satelite/engineer-cli

Now you can setup your first project!

Satelite Engineer allows you to adopt it incrementally into any existing project or to start a project from scratch, by running the following in your command line interface:

mkdir satelite-engineer-demo
cd satelite-engineer-demo
engineer init

This command will list all the available (official) templates, but for now just choose "Initialize on current folder"

This will create an engineer.config.js file that includes a minimal working configuration at the root of your project.

It will also create an.satelite/engineer/ folder containing an example data.json file and a files subfolder.

The latter contains an example code.js file that implements template directives, and finally an example example.plugin.js to demonstrate how would you add or extend Satelite Engineer features.

.
├── engineer.config.js
├── .satelite
│   └── engineer
│        └── data.json
│        └── files
│             └── code.js
│   └── plugins
│       └── example.plugin.js

Now you can run:

engineer build
node src/index.js

// Hello World!

And thats it! you should now see a src folder containing an index.js file and you can proudly go add "Satelite Engineer developer" to your bio on every social media platform.

But Erick, what's so special about it? I could've just wrote a console.log("Hello, World!") myself 🥱

Continue exploring this guide so you'll soon know why this is so much cooler than writing your own static code.

First, let me explain*what just happened.**

Understanding the configuration file

Data sources

Open engineer.config.js and you'll notice that the code imports the example data.json file that was created on initialization:

// ./engineer.config.js
{
    "data" : require("././satelite/engineer/data.json")
}

Which right now only contains the following:

// ./.satelite/engineer/data.json

{
    "message" : "Hello, World!"
}

In short, the*configuration file** should export a configuration Object or a Promise that will return a configuration Object, allowing you to fetch data or configurations asynchronously.

While there aren't (almost) any rules about what goes into a configuration object, your data needs to be put into a data key on your configuration object.

Please note that this could be anything you can get into a NodeJS application (be it by using require/import to fetch some file, fs to read a folder full of markdown files or even fetching data from any remote endpoint).

Template files

Remember I just said there weren't almost any rules? Well, here is the only other exception. To add template files that are going to be processed by Satelite Engineer you need to specify their source and destination paths in a templates key on your configuration object.

// ./engineer.config.js

{
"data" : require("./.satelite/engineer/data.json"),
"templates" : [
    {
        "src" : ".satelite/engineer/files/code.js",
        "dest" : "src/index.js"
    }
]
}

A file template is any file which implements a templating engine. By default, Satelite Engineer will use Handlebars.js to compile your templates, although you can setup any other template engine if you prefer.

// ./.satelite/engineer/files/code.js

console.log("{{message}}"); // 😉 this is how we got our "Hello, World!".

Options

Engineer also allows for an options object, which currently only expects an optional engine key in which you can provide your own Handlebars instance to use instead of the one that comes built-in. This is intended to allow the use of custom handlebars helpers and could be used as follows:

const Handlebars = require('handlebars');

// Extracted from https://github.com/helpers/handlebars-helpers/blob/master/lib/inflection.js#L58
const inflect = function(count, singular, plural, includeCount) {
  var word = (count > 1 || count === 0) ? plural : singular;
  if (includeCount === true) {
    return String(count) + ' ' + word;
  } else {
    return word;
  }
};

Handlebars.registerHelper('inflect', inflect);

module.exports = () => {
  return {
    data: {
      number: 4,
    },
    templates: [
      {
        src: '.satelite/engineer/files/index.js',
        dest: 'src/index.js',
      },
    ],
    options: {
      engine: Handlebars,
    },
  }
};

And then in your template files you could:

<!-- Also taken from https://github.com/helpers/handlebars-helpers/blob/master/lib/inflection.js#L58  -->

{{inflect 0 'string' 'strings'}}
<!-- "strings" -->
{{inflect 1 'string' 'strings'}}
<!-- "string" -->
{{inflect 1 'string' 'strings' true}}
<!-- "1 string" -->
{{inflect 2 'string' 'strings'}}
<!-- "strings" -->
{{inflect 2 'string' 'strings' true}}
<!-- "2 strings" -->

Now let's use it to make something cooler 🥶

This time you are going to provide an array as data input to one of our files to demonstrate how you can generate almost any code structure dynamically:

// ./.satelite/engineer/data.json

{
    "title":"minimal to do list",
    "schema":[
        {
            "id":"user",
            "fields":[
                {
                    "id":"email",
                    "type":"String"
                },
                {
                    "id":"password",
                    "type":"String"
                }
            ]
        },
        {
            "id":"task",
            "fields":[
                {
                    "id":"text",
                    "type":"String"
                },
                {
                    "id":"isDone",
                    "type":"Boolean"
                }
            ]
        }
    ]
}
// ./engineer.config.js

{
"data" : require("./.satelite/engineer/data.json"),
    "templates" : [
        {
            "src" : ".satelite/engineer/files/code.js",
            "dest" : "src/index.js",
            "key" : "schema"
        }
    ]
}

Each file will get the corresponding key from your data object as input for the template directives.

Since all the generated files will have the exact same name and route each one will overwrite the previous, which is not what we intend.

So at this point I would recommend taking a step back by running engineer cleanup on your current working directory so we let Satelite Engineer clean up the mess for us.

Now, in order to name your files dynamically to avoid the previous situation, you can use any key [here] in your destination paths, as follows:

// ./engineer.config.js

{
"data" : require("./.satelite/engineer/data.json"),
"templates" : [
    {
        "src" : ".satelite/engineer/files/code.js",
        "dest" : "src/[id]/index.js", // you can place inside the brackets any key of the input this file template will get
        "key" : "schema"
    }
]
}

This will output a src/user/index.js and a src/todo/index.js

What if I need to transform the data before the files get it?

Satelite Engineer supports functional extension through the use and writing of plugins. Plugins are just any function that will get the current configuration Object, do any work with it and return it back.

// ./.satelite/engineer/plugins/yourAwesomePlugin.js
const yourAwesomePlugin = (config)=>{
  return  config.schema.map((obj)=>{
    obj.displayName = `${obj.id[0].toUpperCase()}${obj.id.substring(1)}`
    obj.slug = obj.id.split(" ").join("-")
    return obj
  })
}

There are no rules on how to run plugins or where to load them, so you can just do this

// ./engineer.config.js
import yourAwesomePlugin from "./.satelite/engineer/plugins/yourAwesomePlugin.js"

const config = {
"data" : require("./.satelite/engineer/data.json"),
"templates" : [
    {
        "src" : ".satelite/engineer/files/code.js",
        "dest" : "src/[id]/index.js", // you can place inside the brackets any key of the input this file template will get
        "key" : "schema"
    }
]
}

config = yourAwesomePlugin(config);
config = yourOtherAwesomePlugin(config);
return config;

And now you can do the following:

// ./.satelite/engineer/files/code.js

console.log('{{displayName}}')

Or even something like

// ./engineer.config.js

...
"dest" : "src/[displayName]/index.js
...

Roadmap

See the open issues for a list of proposed features (and known issues).

Contributing

Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are*greatly appreciated**.

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

Contact

Erick Ruano - @_erickruano - [email protected]