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

@unshopable/melter

v0.1.0-alpha.44

Published

Shopify Theme build tool and plugin manager which lets you create custom directory structures and add custom file transformations to melt anything into Liquid 🫠

Downloads

25

Readme

melter

Melter is used to compile files into something that is compatible with Shopify's theme architecture. You can interact with melter either from its API or CLI.

Warning Melter is still in alpha. Expect breaking changes with every release. Please report any issues here.

Table of Contents

Getting started

First, let's create a new directory, initialize npm, install melter and its CLI locally:

mkdir melter-basic-demo
cd melter-basic-demo
npm init -y
npm install --save-dev @unshopable/melter @unshopable/melter-cli

Now we'll create the following directory structure, files and their contents:

  melter-basic-demo
  β”œβ”€β”€ node_modules
+ β”œβ”€β”€ src
+ β”‚   └── sections
+ β”‚       └── my-section.liquid
  β”œβ”€β”€ package-lock.json
  └── package.json

src/my-section.liquid

<div>Hello, World!</div>

Throughout the documenation we will use diff blocks to show you what changes we're making to directories, files, and code. For instance:

+ this is something new
- and this is something we removed
  and this is unchanged

Note that is is only a basic example to get you started with melter. Your directory structure will look quite different in production.

Next run:

$ npx melter

...

Compiled with 1 warnings

  ⚠ No config found. Loaded default config. To disable this warning create a custom config.

This should have created a new directory (dist) in the root of your project with the following directory strcuture, files and their contents:

  melter-basic-demo
+ β”œβ”€β”€ dist
+ β”‚   └── sections
+ β”‚       └── my-section.liquid
...

dist/my-section.liquid

<div>Hello, World!</div>

Sure, that's not really exciting but we'll get there. Continue reading this documentation.

Configuration

Since melter doesn't really do anything – or at least nothing useful – without configuration, let's create a config:

  melter-basic-demo
  β”œβ”€β”€ node_modules
  β”œβ”€β”€ src
  β”‚   └── sections
  β”‚       └── my-section.liquid
+ β”œβ”€β”€ melter.config.js
  β”œβ”€β”€ package-lock.json
  └── package.json

melter.config.js

/** @type {import("@unshopable/melter").MelterConfig} */
const melterConfig = {};

module.exports = melterConfig;

Options

Input

With the input option you can specify where melter should look for files to compile. Defaults to src.

Output

With the output option you can specify where melter should write the compiled files to. Defaults to dist.

If output is undefined no files will be emitted. You can still access them through compilations stats.

Stats

The stats option controls the built-in StatsPlugin which basically is just a simple logger. Defaults to true.

Setting it to false equals "silent" mode.

Paths

The paths options controls the built-in PathsPlugin which determines where to write files to within the output directory. It's based on Shopify's default theme architecture.

Setting it to false prevents the PathsPlugin from being applied which results in no files being emitted. This is handy if you want to implement a custom directory structure through plugins.

Config Examples

Now that you know what can be configured, let's play with it:

melter.config.js

  /** @type {import("@unshopable/melter").MelterConfig} */
- const melterConfig = {};
+ const melterConfig = {
+   paths: {
+     sections: [
+       /sections\/[^\/]*\.liquid$/,
+       /sections\/legacy\/[^\/]*\.liquid$/,
+     ],
+   },
+ };

  module.exports = melterConfig;

Also update your src directory:

  melter-basic-demo
  β”œβ”€β”€ node_modules
  β”œβ”€β”€ src
  β”‚   └── sections
+ β”‚       β”œβ”€β”€ legacy
+ β”‚       β”‚   └── my-legacy-section.liquid
  β”‚       └── my-section.liquid
  β”œβ”€β”€ melter.config.js
  β”œβ”€β”€ package-lock.json
  └── package.json

And once again, run:

$ npx melter

...

Successfully compiled in x ms

Your dist directory should look like this now:

  melter-basic-demo
  β”œβ”€β”€ dist
  β”‚   └── sections
+ β”‚       β”œβ”€β”€ my-legacy-section.liquid
  β”‚       └── my-section.liquid
...

Warning The PathsPlugin currently does not take care of multiple sections with the same name.

Now extend the paths option so we can split re-usable liquid components and UI components:

  /** @type {import("@unshopable/melter").MelterConfig} */
  const melterConfig = {
    paths: {
      sections: [
        /sections\/[^\/]*\.liquid$/,
        /sections\/legacy\/[^\/]*\.liquid$/,
      ],
+     snippets: [
+       /components\/[^\/]*\.liquid$/,
+       /snippets\/[^\/]*\.liquid$/,
+     ],
    },
  };

  module.exports = melterConfig;

And update your src directory:

  melter-basic-demo
  β”œβ”€β”€ node_modules
  β”œβ”€β”€ src
+ β”‚   β”œβ”€β”€ components
+ β”‚   β”‚   └── my-ui-snippet.liquid
  β”‚   β”œβ”€β”€ sections
  β”‚   β”‚   β”œβ”€β”€ legacy
  β”‚   β”‚   β”‚   └── my-legacy-section.liquid
  β”‚   β”‚   └── my-section.liquid
+ β”‚   └── snippets
+ β”‚       └── my-functional-snippet.liquid
  β”œβ”€β”€ melter.config.js
  β”œβ”€β”€ package-lock.json
  └── package.json

Finally, run:

$ npx melter

...

Successfully compiled in x ms

Your dist directory should look like this now:

  melter-basic-demo
  β”œβ”€β”€ dist
  β”‚   β”œβ”€β”€ sections
  β”‚   β”‚   β”œβ”€β”€ my-legacy-section.liquid
  β”‚   β”‚   └── my-section.liquid
+ β”‚   └── snippets
+ β”‚       β”œβ”€β”€ my-functional-snippet.liquid
+ β”‚       └── my-ui-snippet.liquid
...

If you have custom requirements the base melter config might not be sufficient. In this case consider using an official or community plugin. You can also develop custom plugins.

Plugins

Plugins are what makes melter powerful. A plugin is a JavaScript object that has an apply method which is called by the melter compiler, giving it access to the entire compilation lifecycle.

Plugin Examples

To see them in action, create a new file in your root directory:

  melter-basic-demo
  β”œβ”€β”€ node_modules
  β”œβ”€β”€ src
  β”‚   └── sections
  β”‚       β”œβ”€β”€ legacy
  β”‚       β”‚   └── my-legacy-section.liquid
  β”‚       └── my-section.liquid
  β”œβ”€β”€ melter.config.js
+ β”œβ”€β”€ hello-to-hi-plugin.js
  β”œβ”€β”€ package-lock.json
  └── package.json

hello-to-hi-plugin.js

const { Plugin } = require('@unshopable/melter');

class HelloToHiPlugin extends Plugin {
  apply(compiler) {
    compiler.hooks.emitter.tap('HelloToHiPlugin', (emitter) => {
      emitter.hooks.beforeAssetAction.tap('HelloToHiPlugin', (asset) => {
        const assetContentString = asset.content.toString();

        if (assetContentString.includes('Hello')) {
          const updatedContent = assetContentString.replace('Hello', 'Hi');

          asset.content = Buffer.from(updatedContent);
        }
      });
    });
  }
}

module.exports = HelloToHiPlugin;

Now add this to your melter config:

+ const HelloToHiPlugin = require('./hello-to-hi-plugin.js');

  /** @type {import("@unshopable/melter").MelterConfig} */
  const melterConfig = {
    paths: {
      sections: [
        /sections\/[^\/]*\.liquid$/,
        /sections\/legacy\/[^\/]*\.liquid$/,
      ],
      snippets: [
        /components\/[^\/]*\.liquid$/,
        /snippets\/[^\/]*\.liquid$/,
      ],
    },

+   plugins: [
+     new HelloToHiPlugin(),
+   ],
  };

  module.exports = melterConfig;

Then, run:

$ npx melter

...

Successfully compiled in x ms

Open dist/sections/my-section.liquid. You should see the following content:

<div>Hi, World!</div>

Once again, this is only a super basic example to get to know the capabilities of melter. To see what's possible, check out already existing plugins:

Official Plugins

Community Plugins

  • ...

Custom Plugins

We encourage you to publish your custom plugin(s) so other developers can benefit from them as well.

To make it easy for other developers to find your plugins, please follow these conventions:

  • Prefix it with melter-plugin- (e.g. melter-plugin-hello-to-hi)
  • Include the melter and melter-plugin keyword in your plugin's package.json

Contributing

TODO

License

MIT