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
andmelter-plugin
keyword in your plugin'spackage.json
Contributing
TODO