
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@unshopable/melter
Advanced tools
Shopify Theme build tool and plugin manager which lets you create custom directory structures and add custom file transformations to melt anything into Liquid 🫠
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.
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
diffblocks 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.
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;
With the input option you can specify where melter should look for files to compile. Defaults to src.
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.
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.
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.
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
PathsPlugincurrently 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 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.
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:
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:
melter-plugin- (e.g. melter-plugin-hello-to-hi)melter and melter-plugin keyword in your plugin's package.jsonTODO
FAQs
Shopify Theme build tool and plugin manager which lets you create custom directory structures and add custom file transformations to melt anything into Liquid 🫠
We found that @unshopable/melter demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.