npm package: @molsoft/molsoft-tools
Description: Tools used by the molsoft team to build shopify themes
Author: Tommy Gaudreau
Latest version: 3.1.0

molsoft-tools
=============

> :warning: **These build tools needs a unique key generated by Molsoft to run.**

Tools used by the molsoft team to build shopify themes

* [Usage](#usage)
* [Molsoft tools project structure](#project-structure)
* [Scripts](#scripts)
* [CSS/SCSS](#cssscss)
* [Schema snippets](#schema-snippets)
* [Frameworks](#frameworks)
* [Commands](#commands)


# Usage

Installation:
```sh-session
$ npm install @molsoft/molsoft-tools
```

package.json scripts:
```json
{
  "scripts": {
    "start": "molsoft-tools watch",
    "deploy": "molsoft-tools deploy",
    "build": "molsoft-tools build",
    "watch": "molsoft-tools watch",
    "download": "molsoft-tools download",
    "settings": "molsoft-tools download --settingsOnly",
    "create": "molsoft-tools create",
    "shopify": "shopify"
  }
}
```

config.yml (molsoft-tools will automatically switch to this store):
```yml
store: mystore.myshopify.com
molsoft_key: 123456 # A molsoft key is required to run any command of this package.
```

Usage:
```sh-session
$ npm start - start development server
$ npm run watch - start development server
$ npm run deploy - deploys to production environment
...
```

# Project structure

```
|
└─── src (shopify theme structure)
|   |
│   └─── assets
│   |   └─   ...
|   |
│   └─── config
│   |   └─   ...
|   |
│   └─── layout
│   |   └─   ...
|   |
│   └─── locales
│   |   └─   ...
|   |
│   └─── sections
│   |   └─   ...
|   |
│   └─── snippets
│   |   └─   ...
|   |
│   └─── templates
│   |   └─   ...
|   |
|
└─── tools (molsost tools specific)
|   |
|   └─── schema (used to inject snippets into section schema)
|   |   |
|   |   └─ icons.json
|   |
|   └─── frameworks
|   |   |
|   |   └─── my-framework
|   |   |   |
|   |   |   └─── sections
|   |   |   |   |
|   |   |   |   └─ [name].liquid
|   |   |   |
|   |   |   └─── styles
|   |   |   |   |
|   |   |   |   └─ [name].scss
|   |   |   |
|   |   |   └─── scripts
|   |   |   |   |
|   |   |   |   └─ [name].js
|   |   |   |
|   |   |   └─── snippets
|   |   |   |   |
|   |   |   |   └─ [name]-snippet.liquid
|   |   |   |
|   |   |   └─── rest of the shopify structure
|   |
│   └─── scripts
│   |   │
|   |   └─── layout (webpack entrypoints for layouts, only loaded on specific layout)
|   |   |   |
|   |   |   |- theme.js
|   |   |
|   |   └─── templates (webpack entrypoints for templates, only loaded on specific templates)
|   |   |   |
|   |   |   └─── customers
|   |   |   |   |
|   |   |   |   |- account.js
|   |   |   |   └─ ...
|   |   |   |
|   |   |   |- product.js
|   |   |   |- collection.js
|   |   |   |- index.js
|   |   |   └─ ...
|   |   |
|   |   └─── optimized_section (webpack entrypoints for sections, only loaded where imported)
|   |   |   |
|   |   |   └─ my-section.js
|   |   | 
|   |   └─── optimized_components (webpack entrypoints for components, only loaded where imported)
|   |   |   |
|   |   |   └─ my-component.js
|   |   | 
|   |   └─── personalized-project-structure-folder
|   |   |   |
|   |   |   └─ ...
|   |
│   └─── styles
│   |   │
|   |   └─── sections (suggested structure)
|   |   |   |
|   |   |   └─ ...
|   |   |
|   |   └─── components (suggested structure)
|   |   |   |
|   |   |   └─ ...
|   |   |
|   |   └─── core (suggested structure)
|   |   |   |
|   |   |   └─ ...
|   |   |
|   |   └─── personalized-project-structure-folder
|   |   |   |
|   |   |   └─ ...
|   |   |
│   |   │- theme.scss (suggested structure)
|   |
|
│- package.json
|- .babelrc
|- config.yml   
|- .gitignore
```

# Scripts
There are 4 folders where webpack will look for JS file entry points. molsfot-tools will need to be restarted for any new js files created in these folders.

## `/tools/scripts/layout`
Any scripts that the name match with a shopify layout file will be added to the snippet `script-tags.liquid` and loaded automatically on the matching layout. This should be used for any global scripts across the website.

So a file `/tools/scripts/layout/theme.js` will be loaded on the `theme.liquid` layout with the following logic:
```liquid
{%- if layout == 'theme' or layout == blank -%}
  <script type="text/javascript" src="{{ 'layout.theme.js' | asset_url }}" defer="defer" class="webpack-scripts layout-{{ layout }}"></script>
{%- else -%}
  <link rel="prefetch" href="{{ 'layout.theme.js' | asset_url }}" as="script">
{%- endif -%}
```

For the script to load automatically you will need to add this to your layout file (e.g. theme.liquid):
```liquid
{% render 'script-tags', layout: 'theme' %}
```

## `/tools/scripts/optimized_sections` & `/tools/scripts/optimized_components`
Any scripts in these folders will be added to the snippet `section-script-tags.liquid` and `component-script-tags.liquid`. These optimized scripts needs to be loaded manually in the sections liquid.

A section or component script can be loaded anywhere with the name of the file as a parameter. So a section a file `/tools/scripts/optimized_sections/header.js` or a file `/tools/scripts/optimized_components/tab.js` can be imported like that in a section:
```liquid
{% render 'section-script-tags', section: 'header', lazy: false %}

{% render 'component-script-tags', component: 'tab', lazy: false %}
```

The script will be added like that to the snippet:
```liquid
{%- if section == 'header' -%}
  <script type="{% if lazy %}javascript/blocked{% else %}text/javascript{% endif %}" defer="defer" src="{{ 'section.header.js' | asset_url }}"></script>
{%- endif -%}
```

Notice that you can pass a `lazy` parameter. If set to true the script will be added but never loaded. You will need to add your own logic to load the script at the moment you want. For example, the intersection observer could be used to load the script when the section is close to be in viewport.

## `/tools/scripts/templates` (Deprecated)

> **_NOTE:_** This is marked as deprecated because optimized_sections and optimized_components are prefered for any code related to a section since they are loaded as necessary instead of globally on a template. Using optimized_sections and optimized_components prevents your from loading unnecessary code.

Any scripts that the name match with a shopify template file will be added to the snippet `script-tags.liquid` and loaded automatically on the matching template. 

So a file `/tools/scripts/templates/gift_card.js` will be loaded only on the `gift_card.liquid` template with the following logic:
```liquid
{%- if template == 'gift_card' -%}
  <script type="text/javascript" src="{{ 'template.gift_card.js' | asset_url }}" defer="defer" class="webpack-scripts"></script>
{%- else -%}
  <link rel="prefetch" href="{{ 'template.gift_card.js' | asset_url }}" as="script">
{%- endif -%}
```

For the script to load automatically you will need to add this to your layout file (e.g. theme.liquid):
```liquid
{% render 'script-tags', layout: 'theme' %}
```

# CSS/SCSS
CSS and SCSS can be imported in any of the javascript entry points to be loaded alongside the corresponding script. 

## `/tools/scripts/layout

Having this in the layout script theme.js:
```javascript
import '../../styles/theme.scss';
```

Will add this to the `style-tags.liquid` snippet:
```liquid
{%- if layout == 'theme' -%}
<link type="text/css" href="{{ 'vendors__layout.theme.css' | asset_url }}" rel="stylesheet" class="webpack-styles">
{%- else -%}
<link rel="prefetch" href="{{ 'vendors__layout.theme.css' | asset_url }}" as="style">
{%- endif -%}
```

To have the css load automatically on the layout you will need to add this in the layout file (e.g. theme.liquid):
```liquid
{% render 'style-tags', layout: 'theme' %}
```

## `/tools/scripts/optimized_sections` & `/tools/scripts/optimized_components`

Having this on the section or component script header.js:
```javascript
import '../../styles/sections/header.scss';
```

Will add this to the `section-style-tags.liquid` and `component-style-tags.liquid` snippet:
```liquid
{% if section == 'header' %}
  {% if inline %}
    <style>
      ...
    </style>
  {% else %}
    <link rel="stylesheet" {% if lazy %}data-{% endif %}href="{{ 'section.header.css' | asset_url }}"{% unless lazy %} media="print" onload="this.media='all'"{% endunless %}>
    <noscript>{{ 'section.header.css' | asset_url | stylesheet_tag }}</noscript>
  {% endif %}
{% endif %}
```

A section or component style can be imported anywhere like that with the section name as a parameter:
```liquid
{% render 'section-style-tags', section: 'header', inline: true, lazy: false %}

{% render 'component-style-tags', section: 'tab', inline: true, lazy: false %}
```

Notice that you can pass an `inline` parameter and `lazy` parameter. If inline is set to true the style will be inline. The best practice would be to use a section checkbox to activate this if the section is above the fold and deactivate it if bellow the fold. (For content above the fold it is better for performance to have inline CSS)

Lazy will make the css not load at all and will need some additional logic to load the style when you want to. After some tests this is not a recommended to do as this will cause a flash of unstyled content too big to be user friendly.

## `/tools/scripts/templates`  (Deprecated)

> **_NOTE:_** This is marked as deprecated because optimized_sections and optimized_components are prefered for any code related to a section since they are loaded as necessary instead of globally on a template. Using optimized_sections and optimized_components prevents your from loading unnecessary code.

Having this in the template script product.js:
```javascript
import '../../styles/templates/product.scss';
```

Will add this to the `style-tags.liquid` snippet:
```liquid
{%- if template == 'blog' -%}
  <link type="text/css" href="{{ 'template.blog.css' | asset_url }}" rel="stylesheet" class="webpack-styles">
{%- else -%}
  <link rel="prefetch" href="{{ 'template.blog.css' | asset_url }}" as="style">
{%- endif -%}
```

To have the css load automatically on the template you will need to add this in the layout file (e.g. theme.liquid):
```liquid
{% render 'style-tags', layout: 'theme' %}
```

# schema snippets

molsoft-tools supports the injection of snippets into sections schema using json files. To create a schema snippet, you need to do so in the `/tools/schema` folder. 

## injecting a schema snippet

Create a json file in the folder `/tools/schema`:

e.g.:
```json
// /tools/schema/icons.json
{
  "type": "select",
  "id": "icons",
  "label": "Icons",
  "options": [
    {
      "label": "Search",
      "value": "search"
    },
    {
      "label": "Account",
      "value": "account"
    },
    {
      "label": "Arrow right",
      "value": "arrow-right"
    },
    {
      "label": "Arrow left",
      "value": "arrow-left"
    }
  ]
}
```

Then in your section schema you can inject this snippet using the snippet file name like this:
```liquid
{% schema %}
{
  "name": "My section",
  "class": "my-section",
  "settings": [
    {
      "type": "text",
      "id": "title",
      "label": "Title"
    },
    {
      "snippet": "icons.json"
    },
    {
      "type": "text",
      "id": "other_settings",
      "label": "Some other settings"
    }
  ]
}
{% endschema %}
```

## injecting a schema snippet containing a group of settings

If you want to inject a group of settings all at once, you can create a json file containing an array in the `/tools/schema` folder. 

e.g.:
```json
// /tools/schema/section_layout.json
[
  {
    "type": "header",
    "content": "Layout"
  },
  {
    "type": "checkbox",
    "id": "above_the_fold",
    "label": "Section is above the fold",
    "info": "Section is visible before scrolling the page"
  },
  {
    "type": "select",
    "id": "section_size",
    "label": "Section size",
    "options": [
      {
        "value": "full-width",
        "label": "Full width"
      },
      {
        "value": "full-width-container",
        "label": "Large"
      },
      {
        "value": "container",
        "label": "Medium"
      },
      {
        "value": "container container--small",
        "label": "Small"
      }
    ],
    "default": "container"
  },
  {
    "type": "range",
    "label": "Top spacing",
    "id": "section_margin_top",
    "min": 0,
    "max": 10,
    "unit": "rem",
    "step": 0.2,
    "default": 8
  },
  {
    "type": "range",
    "label": "Section top padding",
    "id": "section_padding_top",
    "min": 0,
    "max": 10,
    "unit": "rem",
    "step": 0.2,
    "default": 6
  },
  {
    "type": "range",
    "label": "Section bottom padding",
    "id": "section_padding_bottom",
    "min": 0,
    "max": 10,
    "unit": "rem",
    "step": 0.2,
    "default": 6
  }
]
```

Then in your section schema you can inject this snippet group by appending `...` in front of the file name:
```liquid
{% schema %}
{
  "name": "My section",
  "class": "my-section",
  "settings": [
    {
      "type": "text",
      "id": "title",
      "label": "Title"
    },
    {
      "snippet": "...section_layout.json"
    },
    {
      "type": "text",
      "id": "other_settings",
      "label": "Some other settings"
    }
  ]
}
{% endschema %}
```

# Frameworks

If you regularly add new sections and their structure is always similar, you can create what we call a framework. A framework is a template of files that will be copied to your project and some variables will be automatically populated. You can check the command [`molsoft-tools create`](#molsoft-tools-create) for more information.

<!-- usagestop -->
# Commands
<!-- commands -->
* [`molsoft-tools watch`](#molsoft-tools-watch)
* [`molsoft-tools deploy`](#molsoft-tools-deploy)
* [`molsoft-tools download`](#molsoft-tools-download)
* [`molsoft-tools create`](#molsoft-tools-create)
* [`molsoft-tools setup-theme`](#molsoft-tools-setup-theme)
* [`molsoft-tools help [COMMAND]`](#molsoft-tools-help-command)

## `molsoft-tools watch`

Will start development server and watch for file changes.

> **Note:** Some issues might occur with the local URL. It is sometimes prefered to use the preview URL.

```
USAGE
  $ molsoft-tools watch

OPTIONS
  -f, --otherFlags=otherFlags  Add other flags to shopify CLI.
                               ex: molsoft-tools watch --otherFlags="--allow-live --dir=something etc...
  -i, --pullId=pullId          The theme id where you want to pull the setting from. Will be prompted to select thetheme if omitted
  -s, --pullSettings           Will pull settings from a selected theme and add it to your development theme
  -v, --verbose                Will log the webpack object (including errors if there is)
```

## `molsoft-tools deploy`

Will build and deploy the theme to the selected theme.

```
USAGE
  $ molsoft-tools deploy

OPTIONS
  -D, --development            Deploy to your development theme
  -b, --buildOnly              Will build the project without deploying
  -d, --distFolder=distFolder  Name of the folder where the project will be built
  -t, --themeId=my-theme-id    The id of the theme where you want to deploy (Shopify cli will prompt you to select a theme if ommited)
  -f, --otherFlags=otherFlags  Add other flags to shopify CLI.
                               ex: molsoft-tools watch --otherFlags="--allow-live --dir=something etc...
  -j, --json                   Creates a json output of the deployment inside the dist folder
  -F, --force                  Will skip the prompt where you are asked if you are sure you want to push to the live theme
  -s, --ignoreSettings         Will deploy the theme without the settings and json templates
  -v, --verbose                Will log the webpack object (including errors if there is)
```

## `molsoft-tools download`

Will download the selected theme into the src folder while ommiting webpack generated files

```
USAGE
  $ molsoft-tools download

OPTIONS
  -t, --themeId=my-theme-id   The id of the theme that you want to download (Shopify cli will prompt you to select a theme if ommited)
  -s, --settingsOnly          Will download only the settings of the specified theme
```

## `molsoft-tools create`

Creates files and folders for a new component based on a framework template.

To create a new framwork template, create a folder in /tools/frameworks with the name you want for your component. In this folder you can replicate the structure you want for your component.

You can use the variable `[name]` in your file names to automatically replace it with the name of the component you are creating. You can also use the variable `[name]` and `[class]` in your file content to automatically replace it with the name of the component you are creating. The variable `[name]` for the file name and the `[class]` variable will be converted to kebab case. The `[name]` variable for the file content will stay the same as the name you entered.

Example of the structure of a framework template:

```
└─── tools
|   |
|   └─── frameworks
|   |   |
|   |   └─── my-framework
|   |   |   |
|   |   |   └─── sections
|   |   |   |   |
|   |   |   |   |- [name].liquid
|   |   |   |
|   |   |   └─── styles
|   |   |   |   |
|   |   |   |   |- [name].scss
|   |   |   |
|   |   |   └─── scripts
|   |   |   |   |
|   |   |   |   |- [name].js
|   |   |   |
|   |   |   └─── snippets
|   |   |   |   |
|   |   |   |   |- [name]-snippet.liquid
|   |   |   |
|   |   |   └─── rest of the shopify structure
```

```
USAGE
  $ molsoft-tools create
```

## `molsoft-tools setup-theme`

Will setup the theme to work with molsoft-tools

```
USAGE
  $ molsoft-tools setup-theme

OPTIONS
  -C, --convert     Will convert a theme in current folder to the molsoft-tools structure.
                    First possible optimization will be to move the scripts and styles in the script and style folders
                    and import them in "scripts/layout/theme.js" and "styles/theme.scss".
  -n, --newProject  Will ask for credentials, download the theme, do the setup and create QA theme and developper theme

DESCRIPTION
  This will convert the default file structure of a shopify theme to the molsoft-tools structure and other small things 
  to help with setup.
```

## `molsoft-tools help [COMMAND]`

display help for molsoft-tools

```
USAGE
  $ molsoft-tools help [COMMAND]

ARGUMENTS
  COMMAND  command to show help for

OPTIONS
  --all  see all commands in CLI
```