
-
{{ product.title }}
+
{{ product.title }}
{%- unless product.available -%}
sold out
{%- endunless -%}
@@ -23,7 +23,7 @@
{%- endunless -%}
{%- endfor -%}
- {%- if settings.pagination -%}
+ {%- if settings.pagination and paginate.pages > 1 -%}
{%- comment -%}Pagination Control in Theme Settings{%- endcomment -%}
{%- if settings.pagination_type == 'accessable_pagination' -%}
{% include 'accessible-pagination' %}
diff --git a/src/components/templates/product.liquid b/src/components/templates/product.liquid
index c600d5d..a450ec9 100644
--- a/src/components/templates/product.liquid
+++ b/src/components/templates/product.liquid
@@ -1,30 +1,63 @@
{% assign current_variant = product.selected_or_first_available_variant %}
{% assign featured_image = current_variant.featured_image | default: product.featured_image %}
-
-

-{% for image in product.images %}
-
-
-
-{% endfor %}
-
{{ product.title }}
-
-
{{ product.description }}
+
+
+
+

+
+ {%- comment -%}
BRAND NAME
{%- endcomment -%}
+
{{ product.title }}
+
{{ product.content }}
+
+
+
+
+
\ No newline at end of file
From b16c84e4745cde7b405fe3ebacbc95b09c365d36 Mon Sep 17 00:00:00 2001
From: Mike
Date: Sat, 28 Nov 2020 12:39:13 -0800
Subject: [PATCH 11/35] ESLint added to webpack
---
.eslintrc | 10 ++++++++++
package.json | 8 ++++++++
src/components/layout/theme.js | 4 ++--
webpack.config.js | 2 +-
4 files changed, 21 insertions(+), 3 deletions(-)
create mode 100644 .eslintrc
diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 0000000..cd0adff
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,10 @@
+{
+ "parser": "babel-eslint",
+ "extends": [
+ "plugin:@shopify/esnext",
+ "plugin:@shopify/node"
+ ],
+ "rules": {
+ "quotes": [2, "single", { "avoidEscape": true }]
+ }
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 1046dbf..7a713f4 100644
--- a/package.json
+++ b/package.json
@@ -19,8 +19,10 @@
"devDependencies": {
"@babel/core": "^7.11.4",
"@babel/preset-env": "^7.11.0",
+ "@shopify/eslint-plugin": "^39.0.3",
"@shopify/themekit": "^1.1.6",
"autoprefixer": "^9.8.6",
+ "babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"babel-plugin-transform-class-properties": "^6.24.1",
"browser-sync": "^2.26.12",
@@ -28,6 +30,12 @@
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^6.3.0",
"css-loader": "^5.0.1",
+ "eslint": "7.2.0",
+ "eslint-loader": "^4.0.2",
+ "eslint-plugin-import": "^2.22.1",
+ "eslint-plugin-jsx-a11y": "^6.4.1",
+ "eslint-plugin-react": "^7.21.5",
+ "eslint-plugin-react-hooks": "4.0.0",
"file-loader": "^6.0.0",
"glob": "^7.1.6",
"html-webpack-plugin": "^4.3.0",
diff --git a/src/components/layout/theme.js b/src/components/layout/theme.js
index fe0fa0c..8213437 100644
--- a/src/components/layout/theme.js
+++ b/src/components/layout/theme.js
@@ -1,4 +1,4 @@
-import "./theme.scss";
+import './theme.scss';
const themeFunction = () => 'Theme JS ES6 Function HMR TEST!';
-console.log(themeFunction())
\ No newline at end of file
+console.log(themeFunction());
diff --git a/webpack.config.js b/webpack.config.js
index 33672ec..335ffdb 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -83,7 +83,7 @@ module.exports = {
{
test: /\.js$/,
exclude: /node_modules/,
- loader: 'babel-loader',
+ use: ["babel-loader", "eslint-loader"]
},
].filter(Boolean),
},
From 5799b7cd3d0a11bddd2ceec29a6490439f92f55c Mon Sep 17 00:00:00 2001
From: Mike
Date: Sat, 28 Nov 2020 13:26:00 -0800
Subject: [PATCH 12/35] Create CONTRIBUTING.md
I figure this is good enough to start
---
CONTRIBUTING.md | 66 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
create mode 100644 CONTRIBUTING.md
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..bb5159c
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,66 @@
+## Contributing
+
+First off, thank you for considering contributing to A Shopify Theme with Themekit and Webpack. It's exciting to see what we can do with modern build tools.
+
+### Where do I go from here?
+
+If you notice a bug or have a feature request, [make one](https://github.com/activeadmin/activeadmin/issues/new)! I'm no pro, creating issues helps improve this repo and helps learn about all that is involved building themes.
+
+### Fork & create a branch
+
+If there is something you think you can fix, then [fork themekit-webpack](https://help.github.com/articles/fork-a-repo) and create
+a branch with a descriptive name.
+
+A good branch name would be (where issue #33 is the ticket you're working on):
+
+```sh
+git checkout -b 33-add-infinite-scroll-feature
+```
+
+### Verify build with ESLint
+When testing your new feature ensure you are passing ESLint rules by running
+```sh
+yarn build
+```
+Make note of any errors that get flagged from ESLint and fix before creating a PR.
+
+### Make a Pull Request
+
+At this point, you should switch back to your master branch and make sure it's
+up to date with themekit-webpack's master branch:
+
+```sh
+git remote add upstream git@github.com:themekit-webpack/themekit-webpack.git
+git checkout master
+git pull upstream master
+```
+
+Then update your feature branch from your local copy of master, and push it!
+
+```sh
+git checkout 33-add-infinite-scroll-feature
+git rebase master
+git push --set-upstream origin 33-add-infinite-scroll-feature
+```
+Finally, go to GitHub and [make a Pull Request](https://help.github.com/articles/creating-a-pull-request) 😎
+### Keeping your Pull Request updated
+
+If a maintainer asks you to "rebase" your PR, they're saying that a lot of code
+has changed, and that you need to update your branch so it's easier to merge.
+
+To learn more about rebasing in Git, there are a lot of good [git rebasing](http://git-scm.com/book/en/Git-Branching-Rebasing) [resources](https://help.github.com/en/github/using-git/about-git-rebase) but here's the suggested workflow:
+
+```sh
+git checkout 33-add-infinite-scroll-feature
+git pull --rebase upstream master
+git push --force-with-lease 33-add-infinite-scroll-feature
+```
+### Merging a PR (maintainers only)
+
+A PR can only be merged into `master` by a maintainer if:
+
+* It is passing ESLint.
+* It has no requested changes.
+* It is up to date with current master.
+
+Any maintainer is allowed to merge a PR if all of these conditions are met.
From 657b80eadfacf2895b3a869eedf75a3a7614d3df Mon Sep 17 00:00:00 2001
From: Mike
Date: Sat, 28 Nov 2020 13:32:30 -0800
Subject: [PATCH 13/35] Create CODE_OF_CONDUCT.md
---
CODE_OF_CONDUCT.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 76 insertions(+)
create mode 100644 CODE_OF_CONDUCT.md
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..f5164c1
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at themekit@3daddict.com. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
From fa8d3b17b9037fc486e1b13a8791dd59be0cb356 Mon Sep 17 00:00:00 2001
From: Mike
Date: Sat, 28 Nov 2020 13:36:27 -0800
Subject: [PATCH 14/35] added refs to conduct
---
CONTRIBUTING.md | 1 +
readme.md | 7 ++++++-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bb5159c..baf31b8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -5,6 +5,7 @@ First off, thank you for considering contributing to A Shopify Theme with Themek
### Where do I go from here?
If you notice a bug or have a feature request, [make one](https://github.com/activeadmin/activeadmin/issues/new)! I'm no pro, creating issues helps improve this repo and helps learn about all that is involved building themes.
+Please make sure to check the [CODE_OF_CONDUCT.md](https://github.com/3daddict/themekit-webpack/blob/master/CODE_OF_CONDUCT.md) before contributing to this repo.
### Fork & create a branch
diff --git a/readme.md b/readme.md
index 13a173d..d62783b 100644
--- a/readme.md
+++ b/readme.md
@@ -3,6 +3,11 @@
# Shopify ThemeKit with Webpack 4
This is a starter Theme using Webpack, ThemeKit and TailwindCSS for developing Shopify themes with modern build tools. The goal is to create a tool with a component-based folder structure and is easy to use.
+## Contributing
+To contribute to this repo please see
+- [CONTRIBUTING.md](https://github.com/3daddict/themekit-webpack/blob/master/CONTRIBUTING.md)
+- [CODE_OF_CONDUCT.md](https://github.com/3daddict/themekit-webpack/blob/master/CODE_OF_CONDUCT.md)
+
## 🎯 Goals
- [x] Component based folder structure
- [x] ES6 Modules
@@ -94,4 +99,4 @@ In the event that you find the HMR assets are not loading and the requests to lo
## 🛣️ Roadmap
- [ ] Finalization and First Release
- [x] Update copy-webpack-plugin to v6 [Issue #519](https://github.com/webpack-contrib/copy-webpack-plugin/issues/519) Thanks [@felixmosh](https://github.com/felixmosh)!
-- [ ] Webpack 5? 🤔
+- [x] Webpack 5? 🤔 Thanks [@felixmosh](https://github.com/felixmosh)!
From 8c46638daf7323da480c0e2524240d520699b9e2 Mon Sep 17 00:00:00 2001
From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com>
Date: Wed, 18 Nov 2020 09:31:28 +0200
Subject: [PATCH 15/35] Finish phase 1, html-hmr
---
liquidDevEntry.js | 55 +++++++++++++++++++++++++++++++++++++++++++++++
webpack.config.js | 4 ++++
2 files changed, 59 insertions(+)
create mode 100644 liquidDevEntry.js
diff --git a/liquidDevEntry.js b/liquidDevEntry.js
new file mode 100644
index 0000000..92f887a
--- /dev/null
+++ b/liquidDevEntry.js
@@ -0,0 +1,55 @@
+const context = require.context('./src', true, /\.liquid$/);
+
+const cache = {};
+
+context.keys().forEach(function (key) {
+ cache[key] = context(key);
+});
+
+function replaceHtml(key, startCommentNode) {
+ const commentNodeType = startCommentNode.nodeType;
+ while (
+ startCommentNode.nextSibling.nodeType !== commentNodeType ||
+ !startCommentNode.nextSibling.nodeValue.includes(`hmr-end: ${key}`)
+ ) {
+ startCommentNode.nextSibling.remove();
+ }
+
+ const tpl = document.createElement('template');
+ tpl.innerHTML = cache[key];
+ startCommentNode.parentNode.insertBefore(tpl.content, startCommentNode.nextSibling);
+}
+
+if (module.hot) {
+ module.hot.accept(context.id, function () {
+ const newContext = require.context('./src', true, /\.liquid$/);
+ const changes = [];
+ newContext.keys().forEach(function (key) {
+ const newFile = newContext(key);
+ if (cache[key] !== newFile) {
+ changes.push(key);
+ cache[key] = newFile;
+ }
+ });
+
+ changes.forEach((changedFile) => {
+ traverseHMRComments(changedFile, replaceHtml);
+ });
+ });
+}
+
+function traverseHMRComments(file, callback) {
+ const nodeIterator = document.createNodeIterator(
+ document.body,
+ NodeFilter.SHOW_COMMENT,
+ function (node) {
+ return node.nodeValue.includes(`hmr-start: ${file}`)
+ ? NodeFilter.FILTER_ACCEPT
+ : NodeFilter.FILTER_REJECT;
+ }
+ );
+
+ while (nodeIterator.nextNode()) {
+ callback(file, nodeIterator.referenceNode);
+ }
+}
diff --git a/webpack.config.js b/webpack.config.js
index 335ffdb..0df5606 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -53,6 +53,10 @@ module.exports = {
},
],
},
+ {
+ test: /\.liquid$/,
+ use: ['string-loader'],
+ },
{
test: /\.(sc|sa|c)ss$/,
use: [
From 62f754b1b7d236da1068170355fc591c5c11410a Mon Sep 17 00:00:00 2001
From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com>
Date: Thu, 19 Nov 2020 10:26:13 +0200
Subject: [PATCH 16/35] Add basic liquid process in the pipeline
---
liquidDevEntry.js => liquidDev.entry.js | 4 +-
liquidDev.loader.js | 51 +++++++++++++++++++++++++
webpack.config.js | 8 +++-
3 files changed, 60 insertions(+), 3 deletions(-)
rename liquidDevEntry.js => liquidDev.entry.js (91%)
create mode 100644 liquidDev.loader.js
diff --git a/liquidDevEntry.js b/liquidDev.entry.js
similarity index 91%
rename from liquidDevEntry.js
rename to liquidDev.entry.js
index 92f887a..5276ca8 100644
--- a/liquidDevEntry.js
+++ b/liquidDev.entry.js
@@ -1,4 +1,4 @@
-const context = require.context('./src', true, /\.liquid$/);
+const context = require.context('./src', true, /message\.liquid$/);
const cache = {};
@@ -22,7 +22,7 @@ function replaceHtml(key, startCommentNode) {
if (module.hot) {
module.hot.accept(context.id, function () {
- const newContext = require.context('./src', true, /\.liquid$/);
+ const newContext = require.context('./src', true, /message\.liquid$/);
const changes = [];
newContext.keys().forEach(function (key) {
const newFile = newContext(key);
diff --git a/liquidDev.loader.js b/liquidDev.loader.js
new file mode 100644
index 0000000..6af258e
--- /dev/null
+++ b/liquidDev.loader.js
@@ -0,0 +1,51 @@
+const loaderUtils = require('loader-utils');
+const path = require('path');
+const { Liquid } = require('liquidjs');
+const glob = require('glob');
+const { liquidSectionTags } = require('liquidjs-section-tags');
+
+const liquidFiles = [
+ ...glob
+ .sync('./src/components/**/*.liquid')
+ .map((filePath) => path.resolve(__dirname, path.dirname(filePath)))
+ .reduce((set, dir) => {
+ set.add(dir);
+ return set;
+ }, new Set()),
+];
+
+const engine = new Liquid({
+ root: liquidFiles, // root for layouts/includes lookup
+ extname: '.liquid', // used for layouts/includes, defaults ""
+});
+
+engine.registerFilter('asset_url', function (v) {
+ const { publicPath } = this.context.opts.loaderOptions;
+
+ return `${publicPath}${v}`;
+});
+
+engine.registerFilter('stylesheet_tag', function (v) {
+ return ``; // in Dev mode we load css from js for HMR
+});
+
+engine.registerFilter('script_tag', function (v) {
+ return ``;
+});
+
+engine.plugin(
+ liquidSectionTags({
+ root: liquidFiles,
+ })
+);
+
+module.exports = function (content) {
+ if (this.cacheable) this.cacheable();
+
+ engine.options.loaderOptions = loaderUtils.getOptions(this);
+ const callback = this.async();
+
+ return engine
+ .parseAndRender(content, engine.options.loaderOptions.globals || {})
+ .then((result) => callback(null, result));
+};
diff --git a/webpack.config.js b/webpack.config.js
index 0df5606..340ef2e 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -55,7 +55,13 @@ module.exports = {
},
{
test: /\.liquid$/,
- use: ['string-loader'],
+ use: [
+ 'string-loader',
+ {
+ loader: path.resolve(__dirname, 'liquidDev.loader.js'),
+ options: { publicPath },
+ },
+ ],
},
{
test: /\.(sc|sa|c)ss$/,
From 932e5e309316ec3f01698848bad4d52d2e5044a0 Mon Sep 17 00:00:00 2001
From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com>
Date: Tue, 24 Nov 2020 00:49:33 +0200
Subject: [PATCH 17/35] Add support for sections & schema tags
---
liquidDev.entry.js | 55 ----------------------------
liquidDev.loader.js | 51 --------------------------
shopify-dev-utils/liquidDev.entry.js | 12 +-----
webpack.config.js | 11 +++++-
4 files changed, 11 insertions(+), 118 deletions(-)
delete mode 100644 liquidDev.entry.js
delete mode 100644 liquidDev.loader.js
diff --git a/liquidDev.entry.js b/liquidDev.entry.js
deleted file mode 100644
index 5276ca8..0000000
--- a/liquidDev.entry.js
+++ /dev/null
@@ -1,55 +0,0 @@
-const context = require.context('./src', true, /message\.liquid$/);
-
-const cache = {};
-
-context.keys().forEach(function (key) {
- cache[key] = context(key);
-});
-
-function replaceHtml(key, startCommentNode) {
- const commentNodeType = startCommentNode.nodeType;
- while (
- startCommentNode.nextSibling.nodeType !== commentNodeType ||
- !startCommentNode.nextSibling.nodeValue.includes(`hmr-end: ${key}`)
- ) {
- startCommentNode.nextSibling.remove();
- }
-
- const tpl = document.createElement('template');
- tpl.innerHTML = cache[key];
- startCommentNode.parentNode.insertBefore(tpl.content, startCommentNode.nextSibling);
-}
-
-if (module.hot) {
- module.hot.accept(context.id, function () {
- const newContext = require.context('./src', true, /message\.liquid$/);
- const changes = [];
- newContext.keys().forEach(function (key) {
- const newFile = newContext(key);
- if (cache[key] !== newFile) {
- changes.push(key);
- cache[key] = newFile;
- }
- });
-
- changes.forEach((changedFile) => {
- traverseHMRComments(changedFile, replaceHtml);
- });
- });
-}
-
-function traverseHMRComments(file, callback) {
- const nodeIterator = document.createNodeIterator(
- document.body,
- NodeFilter.SHOW_COMMENT,
- function (node) {
- return node.nodeValue.includes(`hmr-start: ${file}`)
- ? NodeFilter.FILTER_ACCEPT
- : NodeFilter.FILTER_REJECT;
- }
- );
-
- while (nodeIterator.nextNode()) {
- callback(file, nodeIterator.referenceNode);
- }
-}
diff --git a/liquidDev.loader.js b/liquidDev.loader.js
deleted file mode 100644
index 6af258e..0000000
--- a/liquidDev.loader.js
+++ /dev/null
@@ -1,51 +0,0 @@
-const loaderUtils = require('loader-utils');
-const path = require('path');
-const { Liquid } = require('liquidjs');
-const glob = require('glob');
-const { liquidSectionTags } = require('liquidjs-section-tags');
-
-const liquidFiles = [
- ...glob
- .sync('./src/components/**/*.liquid')
- .map((filePath) => path.resolve(__dirname, path.dirname(filePath)))
- .reduce((set, dir) => {
- set.add(dir);
- return set;
- }, new Set()),
-];
-
-const engine = new Liquid({
- root: liquidFiles, // root for layouts/includes lookup
- extname: '.liquid', // used for layouts/includes, defaults ""
-});
-
-engine.registerFilter('asset_url', function (v) {
- const { publicPath } = this.context.opts.loaderOptions;
-
- return `${publicPath}${v}`;
-});
-
-engine.registerFilter('stylesheet_tag', function (v) {
- return ``; // in Dev mode we load css from js for HMR
-});
-
-engine.registerFilter('script_tag', function (v) {
- return ``;
-});
-
-engine.plugin(
- liquidSectionTags({
- root: liquidFiles,
- })
-);
-
-module.exports = function (content) {
- if (this.cacheable) this.cacheable();
-
- engine.options.loaderOptions = loaderUtils.getOptions(this);
- const callback = this.async();
-
- return engine
- .parseAndRender(content, engine.options.loaderOptions.globals || {})
- .then((result) => callback(null, result));
-};
diff --git a/shopify-dev-utils/liquidDev.entry.js b/shopify-dev-utils/liquidDev.entry.js
index bbbf9ab..a5cdd11 100644
--- a/shopify-dev-utils/liquidDev.entry.js
+++ b/shopify-dev-utils/liquidDev.entry.js
@@ -1,8 +1,4 @@
-const context = require.context(
- '../src',
- true,
- /(collection|footer|featured-product|featured-collection|header|message)\.liquid$/
-);
+const context = require.context('../src', true, /(header|message)\.liquid$/);
const cache = {};
@@ -26,11 +22,7 @@ function replaceHtml(key, startCommentNode) {
if (module.hot) {
module.hot.accept(context.id, function () {
- const newContext = require.context(
- '../src',
- true,
- /(collection|footer|featured-product|featured-collection|header|message)\.liquid$/
- );
+ const newContext = require.context('../src', true, /(header|message)\.liquid$/);
const changes = [];
newContext.keys().forEach(function (key) {
const newFile = newContext(key);
diff --git a/webpack.config.js b/webpack.config.js
index 340ef2e..1d0b5db 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -58,8 +58,15 @@ module.exports = {
use: [
'string-loader',
{
- loader: path.resolve(__dirname, 'liquidDev.loader.js'),
- options: { publicPath },
+ loader: path.resolve(__dirname, 'shopify-dev-utils/liquidDev.loader.js'),
+ options: {
+ publicPath,
+ isSection(liquidPath) {
+ const diff = path.relative(path.join(__dirname, './src/components/'), liquidPath);
+ const componentType = diff.split(path.sep).shift();
+ return componentType === 'sections';
+ }
+ },
},
],
},
From dd4966ae7f4b63b456268b24090b96d9c609af39 Mon Sep 17 00:00:00 2001
From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com>
Date: Wed, 18 Nov 2020 09:31:28 +0200
Subject: [PATCH 18/35] Finish phase 1, html-hmr
---
liquidDevEntry.js | 55 ++++++++++++++++++++++++++++++
package.json | 1 +
src/components/layout/theme.liquid | 4 ++-
webpack.config.js | 34 ++++++++++++------
4 files changed, 82 insertions(+), 12 deletions(-)
create mode 100644 liquidDevEntry.js
diff --git a/liquidDevEntry.js b/liquidDevEntry.js
new file mode 100644
index 0000000..92f887a
--- /dev/null
+++ b/liquidDevEntry.js
@@ -0,0 +1,55 @@
+const context = require.context('./src', true, /\.liquid$/);
+
+const cache = {};
+
+context.keys().forEach(function (key) {
+ cache[key] = context(key);
+});
+
+function replaceHtml(key, startCommentNode) {
+ const commentNodeType = startCommentNode.nodeType;
+ while (
+ startCommentNode.nextSibling.nodeType !== commentNodeType ||
+ !startCommentNode.nextSibling.nodeValue.includes(`hmr-end: ${key}`)
+ ) {
+ startCommentNode.nextSibling.remove();
+ }
+
+ const tpl = document.createElement('template');
+ tpl.innerHTML = cache[key];
+ startCommentNode.parentNode.insertBefore(tpl.content, startCommentNode.nextSibling);
+}
+
+if (module.hot) {
+ module.hot.accept(context.id, function () {
+ const newContext = require.context('./src', true, /\.liquid$/);
+ const changes = [];
+ newContext.keys().forEach(function (key) {
+ const newFile = newContext(key);
+ if (cache[key] !== newFile) {
+ changes.push(key);
+ cache[key] = newFile;
+ }
+ });
+
+ changes.forEach((changedFile) => {
+ traverseHMRComments(changedFile, replaceHtml);
+ });
+ });
+}
+
+function traverseHMRComments(file, callback) {
+ const nodeIterator = document.createNodeIterator(
+ document.body,
+ NodeFilter.SHOW_COMMENT,
+ function (node) {
+ return node.nodeValue.includes(`hmr-start: ${file}`)
+ ? NodeFilter.FILTER_ACCEPT
+ : NodeFilter.FILTER_REJECT;
+ }
+ );
+
+ while (nodeIterator.nextNode()) {
+ callback(file, nodeIterator.referenceNode);
+ }
+}
diff --git a/package.json b/package.json
index 3ad3bd3..ccb3498 100644
--- a/package.json
+++ b/package.json
@@ -42,6 +42,7 @@
"postcss-loader": "^4.0.4",
"prettier": "^2.1.2",
"sass-loader": "^10.1.0",
+ "string-loader": "^0.0.1",
"style-loader": "^2.0.0",
"tailwindcss": "^2.0.1",
"transform-class-properties": "^1.0.0-beta",
diff --git a/src/components/layout/theme.liquid b/src/components/layout/theme.liquid
index 67cc6d3..127d214 100644
--- a/src/components/layout/theme.liquid
+++ b/src/components/layout/theme.liquid
@@ -31,9 +31,11 @@
{%- comment -%}Varibles{%- endcomment -%}
{{ content_for_header }}
{% include 'global-css' %}
-
+
{{ 'tailwind.min.css' | asset_url | stylesheet_tag }}
{{ 'bundle.global-css.css' | asset_url | stylesheet_tag }}
+ {{ 'bundle.runtime.js' | asset_url | script_tag }}
+ {{ 'bundle.liquidDevEntry.js' | asset_url | script_tag }}
{{ 'bundle.theme.js' | asset_url | script_tag }}
diff --git a/webpack.config.js b/webpack.config.js
index 086f4f5..4adc24f 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -15,21 +15,31 @@ const publicPath = isDevMode ? `https://localhost:${port}/` : '';
module.exports = {
stats: stats,
- entry: glob.sync('./src/components/**/*.js').reduce((acc, path) => {
- const entry = path.replace(/^.*[\\\/]/, '').replace('.js', '');
- acc[entry] = path;
- return acc;
- }, {}),
+ entry: glob.sync('./src/components/**/*.js').reduce(
+ (acc, path) => {
+ const entry = path.replace(/^.*[\\\/]/, '').replace('.js', '');
+ acc[entry] = path;
+ return acc;
+ },
+ { liquidDevEntry: './liquidDevEntry.js' }
+ ),
output: {
- filename: './assets/bundle.[name].js',
- hotUpdateChunkFilename: './hot/[id].[fullhash].hot-update.js',
- hotUpdateMainFilename: './hot/[fullhash].hot-update.json',
+ filename: 'assets/bundle.[name].js',
+ hotUpdateChunkFilename: 'hot/[id].[fullhash].hot-update.js',
+ hotUpdateMainFilename: 'hot/[fullhash].hot-update.json',
path: path.resolve(__dirname, 'dist'),
publicPath,
},
cache: false,
+ optimization: {
+ runtimeChunk: { name: 'runtime' },
+ },
module: {
rules: [
+ {
+ test: /\.liquid$/,
+ use: ['string-loader'],
+ },
{
test: /\.(sc|sa|c)ss$/,
use: [
@@ -117,7 +127,10 @@ module.exports = {
return path.join(targetFolder, path.basename(absolutePath));
},
transform: isDevMode
- ? function (content) {
+ ? function (content, absolutePath) {
+ const relativePath = path.join(__dirname, 'src');
+ const diff = path.relative(relativePath, absolutePath);
+
content = content
.toString()
.replace(
@@ -134,7 +147,7 @@ module.exports = {
}
);
- return content;
+ return `${content}`;
}
: undefined,
},
@@ -167,7 +180,6 @@ module.exports = {
https: true,
disableHostCheck: true,
hot: true,
- liveReload: false,
overlay: true,
writeToDisk: true,
},
From 7ce4b0bdc4601c19775c1f851ed8bc25b9833241 Mon Sep 17 00:00:00 2001
From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com>
Date: Thu, 19 Nov 2020 10:26:13 +0200
Subject: [PATCH 19/35] Add basic liquid process in the pipeline
---
liquidDevEntry.js => liquidDev.entry.js | 4 +-
liquidDev.loader.js | 51 +++++++++++++++++++++++++
package.json | 2 +
src/components/layout/theme.liquid | 2 +-
webpack.config.js | 15 +++++---
5 files changed, 66 insertions(+), 8 deletions(-)
rename liquidDevEntry.js => liquidDev.entry.js (91%)
create mode 100644 liquidDev.loader.js
diff --git a/liquidDevEntry.js b/liquidDev.entry.js
similarity index 91%
rename from liquidDevEntry.js
rename to liquidDev.entry.js
index 92f887a..5276ca8 100644
--- a/liquidDevEntry.js
+++ b/liquidDev.entry.js
@@ -1,4 +1,4 @@
-const context = require.context('./src', true, /\.liquid$/);
+const context = require.context('./src', true, /message\.liquid$/);
const cache = {};
@@ -22,7 +22,7 @@ function replaceHtml(key, startCommentNode) {
if (module.hot) {
module.hot.accept(context.id, function () {
- const newContext = require.context('./src', true, /\.liquid$/);
+ const newContext = require.context('./src', true, /message\.liquid$/);
const changes = [];
newContext.keys().forEach(function (key) {
const newFile = newContext(key);
diff --git a/liquidDev.loader.js b/liquidDev.loader.js
new file mode 100644
index 0000000..6af258e
--- /dev/null
+++ b/liquidDev.loader.js
@@ -0,0 +1,51 @@
+const loaderUtils = require('loader-utils');
+const path = require('path');
+const { Liquid } = require('liquidjs');
+const glob = require('glob');
+const { liquidSectionTags } = require('liquidjs-section-tags');
+
+const liquidFiles = [
+ ...glob
+ .sync('./src/components/**/*.liquid')
+ .map((filePath) => path.resolve(__dirname, path.dirname(filePath)))
+ .reduce((set, dir) => {
+ set.add(dir);
+ return set;
+ }, new Set()),
+];
+
+const engine = new Liquid({
+ root: liquidFiles, // root for layouts/includes lookup
+ extname: '.liquid', // used for layouts/includes, defaults ""
+});
+
+engine.registerFilter('asset_url', function (v) {
+ const { publicPath } = this.context.opts.loaderOptions;
+
+ return `${publicPath}${v}`;
+});
+
+engine.registerFilter('stylesheet_tag', function (v) {
+ return ``; // in Dev mode we load css from js for HMR
+});
+
+engine.registerFilter('script_tag', function (v) {
+ return ``;
+});
+
+engine.plugin(
+ liquidSectionTags({
+ root: liquidFiles,
+ })
+);
+
+module.exports = function (content) {
+ if (this.cacheable) this.cacheable();
+
+ engine.options.loaderOptions = loaderUtils.getOptions(this);
+ const callback = this.async();
+
+ return engine
+ .parseAndRender(content, engine.options.loaderOptions.globals || {})
+ .then((result) => callback(null, result));
+};
diff --git a/package.json b/package.json
index ccb3498..cddb400 100644
--- a/package.json
+++ b/package.json
@@ -35,6 +35,8 @@
"file-loader": "^6.0.0",
"glob": "^7.1.6",
"html-webpack-plugin": "^4.3.0",
+ "liquidjs": "^9.16.1",
+ "liquidjs-section-tags": "^1.0.0",
"mini-css-extract-plugin": "^1.3.1",
"node-fetch": "^2.6.1",
"node-sass": "^5.0.0",
diff --git a/src/components/layout/theme.liquid b/src/components/layout/theme.liquid
index 127d214..1e8cc9e 100644
--- a/src/components/layout/theme.liquid
+++ b/src/components/layout/theme.liquid
@@ -35,7 +35,7 @@
{{ 'tailwind.min.css' | asset_url | stylesheet_tag }}
{{ 'bundle.global-css.css' | asset_url | stylesheet_tag }}
{{ 'bundle.runtime.js' | asset_url | script_tag }}
- {{ 'bundle.liquidDevEntry.js' | asset_url | script_tag }}
+ {{ 'bundle.liquidDev.js' | asset_url | script_tag }}
{{ 'bundle.theme.js' | asset_url | script_tag }}
diff --git a/webpack.config.js b/webpack.config.js
index 4adc24f..dae9e10 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -21,7 +21,7 @@ module.exports = {
acc[entry] = path;
return acc;
},
- { liquidDevEntry: './liquidDevEntry.js' }
+ { liquidDev: './liquidDev.entry.js' }
),
output: {
filename: 'assets/bundle.[name].js',
@@ -36,9 +36,15 @@ module.exports = {
},
module: {
rules: [
- {
+ isDevMode && {
test: /\.liquid$/,
- use: ['string-loader'],
+ use: [
+ 'string-loader',
+ {
+ loader: path.resolve(__dirname, 'liquidDev.loader.js'),
+ options: { publicPath },
+ },
+ ],
},
{
test: /\.(sc|sa|c)ss$/,
@@ -72,8 +78,7 @@ module.exports = {
exclude: /node_modules/,
use: ["babel-loader"]
},
-
- ],
+ ].filter(Boolean),
},
plugins: [
new CleanWebpackPlugin({
From 289ba439acc3df8f30b12e515eb848517dbc6577 Mon Sep 17 00:00:00 2001
From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com>
Date: Tue, 24 Nov 2020 00:49:33 +0200
Subject: [PATCH 20/35] Add support for sections & schema tags
---
liquidDev.loader.js | 51 ----------------
package.json | 1 -
.../liquidDev.entry.js | 4 +-
shopify-dev-utils/liquidDev.loader.js | 59 +++++++++++++++++++
shopify-dev-utils/section-tags/index.d.ts | 2 +
shopify-dev-utils/section-tags/index.js | 16 +++++
.../section-tags/javascript.d.ts | 2 +
shopify-dev-utils/section-tags/javascript.js | 24 ++++++++
shopify-dev-utils/section-tags/schema.d.ts | 2 +
shopify-dev-utils/section-tags/schema.js | 45 ++++++++++++++
shopify-dev-utils/section-tags/section.d.ts | 2 +
shopify-dev-utils/section-tags/section.js | 30 ++++++++++
.../section-tags/stylesheet.d.ts | 2 +
shopify-dev-utils/section-tags/stylesheet.js | 44 ++++++++++++++
shopify-dev-utils/transformLiquid.js | 31 ++++++++++
webpack.config.js | 39 ++++--------
16 files changed, 273 insertions(+), 81 deletions(-)
delete mode 100644 liquidDev.loader.js
rename liquidDev.entry.js => shopify-dev-utils/liquidDev.entry.js (89%)
create mode 100644 shopify-dev-utils/liquidDev.loader.js
create mode 100644 shopify-dev-utils/section-tags/index.d.ts
create mode 100644 shopify-dev-utils/section-tags/index.js
create mode 100644 shopify-dev-utils/section-tags/javascript.d.ts
create mode 100644 shopify-dev-utils/section-tags/javascript.js
create mode 100644 shopify-dev-utils/section-tags/schema.d.ts
create mode 100644 shopify-dev-utils/section-tags/schema.js
create mode 100644 shopify-dev-utils/section-tags/section.d.ts
create mode 100644 shopify-dev-utils/section-tags/section.js
create mode 100644 shopify-dev-utils/section-tags/stylesheet.d.ts
create mode 100644 shopify-dev-utils/section-tags/stylesheet.js
create mode 100644 shopify-dev-utils/transformLiquid.js
diff --git a/liquidDev.loader.js b/liquidDev.loader.js
deleted file mode 100644
index 6af258e..0000000
--- a/liquidDev.loader.js
+++ /dev/null
@@ -1,51 +0,0 @@
-const loaderUtils = require('loader-utils');
-const path = require('path');
-const { Liquid } = require('liquidjs');
-const glob = require('glob');
-const { liquidSectionTags } = require('liquidjs-section-tags');
-
-const liquidFiles = [
- ...glob
- .sync('./src/components/**/*.liquid')
- .map((filePath) => path.resolve(__dirname, path.dirname(filePath)))
- .reduce((set, dir) => {
- set.add(dir);
- return set;
- }, new Set()),
-];
-
-const engine = new Liquid({
- root: liquidFiles, // root for layouts/includes lookup
- extname: '.liquid', // used for layouts/includes, defaults ""
-});
-
-engine.registerFilter('asset_url', function (v) {
- const { publicPath } = this.context.opts.loaderOptions;
-
- return `${publicPath}${v}`;
-});
-
-engine.registerFilter('stylesheet_tag', function (v) {
- return ``; // in Dev mode we load css from js for HMR
-});
-
-engine.registerFilter('script_tag', function (v) {
- return ``;
-});
-
-engine.plugin(
- liquidSectionTags({
- root: liquidFiles,
- })
-);
-
-module.exports = function (content) {
- if (this.cacheable) this.cacheable();
-
- engine.options.loaderOptions = loaderUtils.getOptions(this);
- const callback = this.async();
-
- return engine
- .parseAndRender(content, engine.options.loaderOptions.globals || {})
- .then((result) => callback(null, result));
-};
diff --git a/package.json b/package.json
index cddb400..197a2f1 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,6 @@
"glob": "^7.1.6",
"html-webpack-plugin": "^4.3.0",
"liquidjs": "^9.16.1",
- "liquidjs-section-tags": "^1.0.0",
"mini-css-extract-plugin": "^1.3.1",
"node-fetch": "^2.6.1",
"node-sass": "^5.0.0",
diff --git a/liquidDev.entry.js b/shopify-dev-utils/liquidDev.entry.js
similarity index 89%
rename from liquidDev.entry.js
rename to shopify-dev-utils/liquidDev.entry.js
index 5276ca8..a5cdd11 100644
--- a/liquidDev.entry.js
+++ b/shopify-dev-utils/liquidDev.entry.js
@@ -1,4 +1,4 @@
-const context = require.context('./src', true, /message\.liquid$/);
+const context = require.context('../src', true, /(header|message)\.liquid$/);
const cache = {};
@@ -22,7 +22,7 @@ function replaceHtml(key, startCommentNode) {
if (module.hot) {
module.hot.accept(context.id, function () {
- const newContext = require.context('./src', true, /message\.liquid$/);
+ const newContext = require.context('../src', true, /(header|message)\.liquid$/);
const changes = [];
newContext.keys().forEach(function (key) {
const newFile = newContext(key);
diff --git a/shopify-dev-utils/liquidDev.loader.js b/shopify-dev-utils/liquidDev.loader.js
new file mode 100644
index 0000000..cf31d5f
--- /dev/null
+++ b/shopify-dev-utils/liquidDev.loader.js
@@ -0,0 +1,59 @@
+const loaderUtils = require('loader-utils');
+const path = require('path');
+const { Liquid } = require('liquidjs');
+const glob = require('glob');
+const { liquidSectionTags } = require('./section-tags/index');
+
+const liquidFiles = [
+ ...glob
+ .sync('./src/components/**/*.liquid')
+ .map((filePath) => path.resolve(path.join(__dirname, '../'), path.dirname(filePath)))
+ .reduce((set, dir) => {
+ set.add(dir);
+ return set;
+ }, new Set()),
+];
+
+const engine = new Liquid({
+ root: liquidFiles, // root for layouts/includes lookup
+ extname: '.liquid', // used for layouts/includes, defaults ""
+});
+
+engine.registerFilter('asset_url', function (v) {
+ const { publicPath } = this.context.opts.loaderOptions;
+
+ return `${publicPath}${v}`;
+});
+
+engine.registerFilter('paginate', function (_v) {
+ return ``; // in Dev mode we load css from js for HMR
+});
+
+engine.registerFilter('stylesheet_tag', function (_v) {
+ return ``; // in Dev mode we load css from js for HMR
+});
+
+engine.registerFilter('script_tag', function (v) {
+ return ``;
+});
+
+engine.plugin(liquidSectionTags());
+
+module.exports = function (content) {
+ if (this.cacheable) this.cacheable();
+
+ engine.options.loaderOptions = loaderUtils.getOptions(this);
+ const { isSection } = engine.options.loaderOptions;
+
+ // section handled specially
+ if (typeof isSection === 'function' && isSection(this.context)) {
+ const sectionName = path.basename(this.resourcePath, '.liquid');
+ content = `{% section "${sectionName}" %}`;
+ }
+
+ const callback = this.async();
+
+ return engine
+ .parseAndRender(content, engine.options.loaderOptions.globals || {})
+ .then((result) => callback(null, result));
+};
diff --git a/shopify-dev-utils/section-tags/index.d.ts b/shopify-dev-utils/section-tags/index.d.ts
new file mode 100644
index 0000000..49e17b0
--- /dev/null
+++ b/shopify-dev-utils/section-tags/index.d.ts
@@ -0,0 +1,2 @@
+import { Liquid } from 'liquidjs';
+export declare function liquidSectionTags(): (this: Liquid) => void;
diff --git a/shopify-dev-utils/section-tags/index.js b/shopify-dev-utils/section-tags/index.js
new file mode 100644
index 0000000..d0f109e
--- /dev/null
+++ b/shopify-dev-utils/section-tags/index.js
@@ -0,0 +1,16 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.liquidSectionTags = void 0;
+const javascript_1 = require("./javascript");
+const schema_1 = require("./schema");
+const section_1 = require("./section");
+const stylesheet_1 = require("./stylesheet");
+function liquidSectionTags() {
+ return function () {
+ this.registerTag('section', section_1.Section);
+ this.registerTag('schema', schema_1.Schema);
+ this.registerTag('stylesheet', stylesheet_1.StyleSheet);
+ this.registerTag('javascript', javascript_1.JavaScript);
+ };
+}
+exports.liquidSectionTags = liquidSectionTags;
diff --git a/shopify-dev-utils/section-tags/javascript.d.ts b/shopify-dev-utils/section-tags/javascript.d.ts
new file mode 100644
index 0000000..40a02a3
--- /dev/null
+++ b/shopify-dev-utils/section-tags/javascript.d.ts
@@ -0,0 +1,2 @@
+import { TagImplOptions } from 'liquidjs/dist/template/tag/tag-impl-options';
+export declare const JavaScript: TagImplOptions;
diff --git a/shopify-dev-utils/section-tags/javascript.js b/shopify-dev-utils/section-tags/javascript.js
new file mode 100644
index 0000000..2c89529
--- /dev/null
+++ b/shopify-dev-utils/section-tags/javascript.js
@@ -0,0 +1,24 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.JavaScript = void 0;
+exports.JavaScript = {
+ parse: function (tagToken, remainTokens) {
+ this.tokens = [];
+ const stream = this.liquid.parser.parseStream(remainTokens);
+ stream
+ .on('token', (token) => {
+ if (token.name === 'endjavascript')
+ stream.stop();
+ else
+ this.tokens.push(token);
+ })
+ .on('end', () => {
+ throw new Error(`tag ${tagToken.getText()} not closed`);
+ });
+ stream.start();
+ },
+ render: function () {
+ const text = this.tokens.map((token) => token.getText()).join('');
+ return ``;
+ }
+};
diff --git a/shopify-dev-utils/section-tags/schema.d.ts b/shopify-dev-utils/section-tags/schema.d.ts
new file mode 100644
index 0000000..da4f996
--- /dev/null
+++ b/shopify-dev-utils/section-tags/schema.d.ts
@@ -0,0 +1,2 @@
+import { TagImplOptions } from 'liquidjs/dist/template/tag/tag-impl-options';
+export declare const Schema: TagImplOptions;
diff --git a/shopify-dev-utils/section-tags/schema.js b/shopify-dev-utils/section-tags/schema.js
new file mode 100644
index 0000000..71744e2
--- /dev/null
+++ b/shopify-dev-utils/section-tags/schema.js
@@ -0,0 +1,45 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.Schema = void 0;
+function generateSettingsObj(settings) {
+ if (!Array.isArray(settings)) {
+ return settings;
+ }
+ return settings
+ .filter((entry) => !!entry.id)
+ .reduce((sectionSettings, entry) => {
+ sectionSettings[entry.id] = entry.default;
+ return sectionSettings;
+ }, {});
+}
+exports.Schema = {
+ parse: function (tagToken, remainTokens) {
+ this.tokens = [];
+ const stream = this.liquid.parser.parseStream(remainTokens);
+ stream
+ .on('token', (token) => {
+ if (token.name === 'endschema') {
+ stream.stop();
+ }
+ else
+ this.tokens.push(token);
+ })
+ .on('end', () => {
+ throw new Error(`tag ${tagToken.getText()} not closed`);
+ });
+ stream.start();
+ },
+ render: function (ctx) {
+ const json = this.tokens.map((token) => token.getText()).join('');
+ const schema = JSON.parse(json);
+ const scope = ctx.scopes[ctx.scopes.length - 1];
+ scope.section = {
+ settings: generateSettingsObj(schema.settings),
+ blocks: (schema.blocks || []).map((block) => ({
+ ...block,
+ settings: generateSettingsObj(block.settings)
+ }))
+ };
+ return '';
+ }
+};
diff --git a/shopify-dev-utils/section-tags/section.d.ts b/shopify-dev-utils/section-tags/section.d.ts
new file mode 100644
index 0000000..7770b29
--- /dev/null
+++ b/shopify-dev-utils/section-tags/section.d.ts
@@ -0,0 +1,2 @@
+import { TagImplOptions } from 'liquidjs';
+export declare const Section: TagImplOptions;
diff --git a/shopify-dev-utils/section-tags/section.js b/shopify-dev-utils/section-tags/section.js
new file mode 100644
index 0000000..0baca53
--- /dev/null
+++ b/shopify-dev-utils/section-tags/section.js
@@ -0,0 +1,30 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.Section = void 0;
+const quoted = /^'[^']*'|"[^"]*"$/;
+exports.Section = {
+ parse: function (token) {
+ this.namestr = token.args;
+ },
+ render: function* (ctx, emitter) {
+ let name;
+ if (quoted.exec(this.namestr)) {
+ const template = this.namestr.slice(1, -1);
+ name = yield this.liquid._parseAndRender(template, ctx.getAll(), ctx.opts);
+ }
+ if (!name)
+ throw new Error('cannot include with empty filename');
+ const templates = yield this.liquid._parseFile(name, ctx.opts, ctx.sync);
+ // Bubble up schema tag for allowing it's data available to the section
+ templates.sort((tagA) => {
+ return tagA.token.kind === 4 &&
+ tagA.token.name === 'schema'
+ ? -1
+ : 0;
+ });
+ const scope = {};
+ ctx.push(scope);
+ yield this.liquid.renderer.renderTemplates(templates, ctx, emitter);
+ ctx.pop();
+ }
+};
diff --git a/shopify-dev-utils/section-tags/stylesheet.d.ts b/shopify-dev-utils/section-tags/stylesheet.d.ts
new file mode 100644
index 0000000..c09b425
--- /dev/null
+++ b/shopify-dev-utils/section-tags/stylesheet.d.ts
@@ -0,0 +1,2 @@
+import { TagImplOptions } from 'liquidjs';
+export declare const StyleSheet: TagImplOptions;
diff --git a/shopify-dev-utils/section-tags/stylesheet.js b/shopify-dev-utils/section-tags/stylesheet.js
new file mode 100644
index 0000000..b44809c
--- /dev/null
+++ b/shopify-dev-utils/section-tags/stylesheet.js
@@ -0,0 +1,44 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.StyleSheet = void 0;
+const sass_1 = require("sass");
+const quoted = /^'[^']*'|"[^"]*"$/;
+const processors = {
+ '': (x) => x,
+ sass: sassProcessor,
+ scss: sassProcessor
+};
+exports.StyleSheet = {
+ parse: function (token, remainTokens) {
+ this.processor = token.args;
+ this.tokens = [];
+ const stream = this.liquid.parser.parseStream(remainTokens);
+ stream
+ .on('token', (token) => {
+ if (token.name === 'endstylesheet')
+ stream.stop();
+ else
+ this.tokens.push(token);
+ })
+ .on('end', () => {
+ throw new Error(`tag ${token.getText()} not closed`);
+ });
+ stream.start();
+ },
+ render: async function (ctx) {
+ let processor = '';
+ if (quoted.exec(this.processor)) {
+ const template = this.processor.slice(1, -1);
+ processor = await this.liquid.parseAndRender(template, ctx.getAll(), ctx.opts);
+ }
+ const text = this.tokens.map((token) => token.getText()).join('');
+ const p = processors[processor];
+ if (!p)
+ throw new Error(`processor for ${processor} not found`);
+ const css = await p(text);
+ return ``;
+ }
+};
+function sassProcessor(data) {
+ return new Promise((resolve, reject) => sass_1.render({ data }, (err, result) => err ? reject(err) : resolve('' + result.css)));
+}
diff --git a/shopify-dev-utils/transformLiquid.js b/shopify-dev-utils/transformLiquid.js
new file mode 100644
index 0000000..23d80ed
--- /dev/null
+++ b/shopify-dev-utils/transformLiquid.js
@@ -0,0 +1,31 @@
+const path = require('path');
+
+module.exports.transformLiquid = function transformLiquid(publicPath) {
+ return (content, absolutePath) => {
+ const relativePath = path.join(__dirname, '../src');
+ const diff = path.relative(relativePath, absolutePath);
+
+ content = content
+ .toString()
+ .replace(
+ /{{\s*'([^']+)'\s*\|\s*asset_url\s*\|\s*(stylesheet_tag|script_tag)\s*}}/g,
+ function (matched, fileName, type) {
+ if (type === 'stylesheet_tag') {
+ if (fileName !== 'tailwind.min.css') {
+ return '';
+ }
+ return matched;
+ }
+
+ return ``;
+ }
+ );
+
+ if(diff.includes('/layout/theme.liquid')) {
+ // inject HMR entry bundle
+ content = content.replace('',``)
+ }
+
+ return `${content}`;
+ };
+}
diff --git a/webpack.config.js b/webpack.config.js
index dae9e10..060956b 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -6,6 +6,7 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackShellPluginNext = require('webpack-shell-plugin-next');
const ESLintPlugin = require('eslint-webpack-plugin');
+const { transformLiquid } = require('./shopify-dev-utils/transformLiquid');
const isDevMode = argv.mode === 'development';
@@ -21,7 +22,7 @@ module.exports = {
acc[entry] = path;
return acc;
},
- { liquidDev: './liquidDev.entry.js' }
+ { liquidDev: './shopify-dev-utils/liquidDev.entry.js' }
),
output: {
filename: 'assets/bundle.[name].js',
@@ -41,8 +42,15 @@ module.exports = {
use: [
'string-loader',
{
- loader: path.resolve(__dirname, 'liquidDev.loader.js'),
- options: { publicPath },
+ loader: path.resolve(__dirname, 'shopify-dev-utils/liquidDev.loader.js'),
+ options: {
+ publicPath,
+ isSection(liquidPath) {
+ const diff = path.relative(path.join(__dirname, './src/components/'), liquidPath);
+ const componentType = diff.split(path.sep).shift();
+ return componentType === 'sections';
+ }
+ },
},
],
},
@@ -131,30 +139,7 @@ module.exports = {
const targetFolder = diff.split(path.sep)[0];
return path.join(targetFolder, path.basename(absolutePath));
},
- transform: isDevMode
- ? function (content, absolutePath) {
- const relativePath = path.join(__dirname, 'src');
- const diff = path.relative(relativePath, absolutePath);
-
- content = content
- .toString()
- .replace(
- /{{\s*'([^']+)'\s*\|\s*asset_url\s*\|\s*(stylesheet_tag|script_tag)\s*}}/g,
- function (matched, fileName, type) {
- if (type === 'stylesheet_tag') {
- if (fileName !== 'tailwind.min.css') {
- return '';
- }
- return matched;
- }
-
- return ``;
- }
- );
-
- return `${content}`;
- }
- : undefined,
+ transform: isDevMode ? transformLiquid(publicPath) : undefined,
},
{
from: 'src/assets/**/*',
From d2533c02cd3dcbb2bc05ee23001203cc9301da3e Mon Sep 17 00:00:00 2001
From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com>
Date: Tue, 24 Nov 2020 21:23:25 +0200
Subject: [PATCH 21/35] Add static store data
---
shopify-dev-utils/liquidDev.entry.js | 4 ++--
shopify-dev-utils/liquidDev.loader.js | 6 ++++--
shopify-dev-utils/storeData.js | 26 ++++++++++++++++++++++++++
3 files changed, 32 insertions(+), 4 deletions(-)
create mode 100644 shopify-dev-utils/storeData.js
diff --git a/shopify-dev-utils/liquidDev.entry.js b/shopify-dev-utils/liquidDev.entry.js
index a5cdd11..4eee04f 100644
--- a/shopify-dev-utils/liquidDev.entry.js
+++ b/shopify-dev-utils/liquidDev.entry.js
@@ -1,4 +1,4 @@
-const context = require.context('../src', true, /(header|message)\.liquid$/);
+const context = require.context('../src', true, /(footer|featured-product|featured-collection|header|message)\.liquid$/);
const cache = {};
@@ -22,7 +22,7 @@ function replaceHtml(key, startCommentNode) {
if (module.hot) {
module.hot.accept(context.id, function () {
- const newContext = require.context('../src', true, /(header|message)\.liquid$/);
+ const newContext = require.context('../src', true, /(footer|featured-product|featured-collection|header|message)\.liquid$/);
const changes = [];
newContext.keys().forEach(function (key) {
const newFile = newContext(key);
diff --git a/shopify-dev-utils/liquidDev.loader.js b/shopify-dev-utils/liquidDev.loader.js
index cf31d5f..46f8b84 100644
--- a/shopify-dev-utils/liquidDev.loader.js
+++ b/shopify-dev-utils/liquidDev.loader.js
@@ -2,6 +2,7 @@ const loaderUtils = require('loader-utils');
const path = require('path');
const { Liquid } = require('liquidjs');
const glob = require('glob');
+const { fetchStoreData } = require('./storeData');
const { liquidSectionTags } = require('./section-tags/index');
const liquidFiles = [
@@ -16,7 +17,8 @@ const liquidFiles = [
const engine = new Liquid({
root: liquidFiles, // root for layouts/includes lookup
- extname: '.liquid', // used for layouts/includes, defaults ""
+ extname: '.liquid', // used for layouts/includes, defaults "",
+ globals: fetchStoreData()
});
engine.registerFilter('asset_url', function (v) {
@@ -26,7 +28,7 @@ engine.registerFilter('asset_url', function (v) {
});
engine.registerFilter('paginate', function (_v) {
- return ``; // in Dev mode we load css from js for HMR
+ return ``;
});
engine.registerFilter('stylesheet_tag', function (_v) {
diff --git a/shopify-dev-utils/storeData.js b/shopify-dev-utils/storeData.js
new file mode 100644
index 0000000..83af7e2
--- /dev/null
+++ b/shopify-dev-utils/storeData.js
@@ -0,0 +1,26 @@
+module.exports.fetchStoreData = function fetchStoreData() {
+
+ return {
+ 'shop': {
+ 'name': 'themekit-webpack-test'
+ },
+ 'linklists': {
+ 'main-menu': {
+ 'title': '',
+ 'levels': 1,
+ 'links': [
+ {
+ 'title': 'Home',
+ 'url': '/',
+ 'links': []
+ },
+ {
+ 'title': 'Catalog',
+ 'url': '/collections/all',
+ 'links': []
+ }
+ ]
+ }
+ }
+ };
+};
From 5ff723d4d4054218d5ef7ffcff968bd4be1fbbcb Mon Sep 17 00:00:00 2001
From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com>
Date: Sat, 28 Nov 2020 16:41:35 +0200
Subject: [PATCH 22/35] Add support for paginate tag
---
shopify-dev-utils/liquidDev.entry.js | 12 +++-
shopify-dev-utils/liquidDev.loader.js | 58 ++++++++---------
shopify-dev-utils/tags/paginate.d.ts | 2 +
shopify-dev-utils/tags/paginate.js | 93 +++++++++++++++++++++++++++
4 files changed, 133 insertions(+), 32 deletions(-)
create mode 100644 shopify-dev-utils/tags/paginate.d.ts
create mode 100644 shopify-dev-utils/tags/paginate.js
diff --git a/shopify-dev-utils/liquidDev.entry.js b/shopify-dev-utils/liquidDev.entry.js
index 4eee04f..bbbf9ab 100644
--- a/shopify-dev-utils/liquidDev.entry.js
+++ b/shopify-dev-utils/liquidDev.entry.js
@@ -1,4 +1,8 @@
-const context = require.context('../src', true, /(footer|featured-product|featured-collection|header|message)\.liquid$/);
+const context = require.context(
+ '../src',
+ true,
+ /(collection|footer|featured-product|featured-collection|header|message)\.liquid$/
+);
const cache = {};
@@ -22,7 +26,11 @@ function replaceHtml(key, startCommentNode) {
if (module.hot) {
module.hot.accept(context.id, function () {
- const newContext = require.context('../src', true, /(footer|featured-product|featured-collection|header|message)\.liquid$/);
+ const newContext = require.context(
+ '../src',
+ true,
+ /(collection|footer|featured-product|featured-collection|header|message)\.liquid$/
+ );
const changes = [];
newContext.keys().forEach(function (key) {
const newFile = newContext(key);
diff --git a/shopify-dev-utils/liquidDev.loader.js b/shopify-dev-utils/liquidDev.loader.js
index 46f8b84..bfaed8b 100644
--- a/shopify-dev-utils/liquidDev.loader.js
+++ b/shopify-dev-utils/liquidDev.loader.js
@@ -4,58 +4,56 @@ const { Liquid } = require('liquidjs');
const glob = require('glob');
const { fetchStoreData } = require('./storeData');
const { liquidSectionTags } = require('./section-tags/index');
+const { Paginate } = require('./tags/paginate');
const liquidFiles = [
- ...glob
- .sync('./src/components/**/*.liquid')
- .map((filePath) => path.resolve(path.join(__dirname, '../'), path.dirname(filePath)))
- .reduce((set, dir) => {
- set.add(dir);
- return set;
- }, new Set()),
+ ...glob
+ .sync('./src/components/**/*.liquid')
+ .map((filePath) => path.resolve(path.join(__dirname, '../'), path.dirname(filePath)))
+ .reduce((set, dir) => {
+ set.add(dir);
+ return set;
+ }, new Set()),
];
const engine = new Liquid({
- root: liquidFiles, // root for layouts/includes lookup
- extname: '.liquid', // used for layouts/includes, defaults "",
- globals: fetchStoreData()
+ root: liquidFiles, // root for layouts/includes lookup
+ extname: '.liquid', // used for layouts/includes, defaults "",
+ globals: fetchStoreData(),
});
engine.registerFilter('asset_url', function (v) {
- const { publicPath } = this.context.opts.loaderOptions;
+ const { publicPath } = this.context.opts.loaderOptions;
- return `${publicPath}${v}`;
-});
-
-engine.registerFilter('paginate', function (_v) {
- return ``;
+ return `${publicPath}${v}`;
});
engine.registerFilter('stylesheet_tag', function (_v) {
- return ``; // in Dev mode we load css from js for HMR
+ return ``; // in Dev mode we load css from js for HMR
});
engine.registerFilter('script_tag', function (v) {
- return ``;
+ return ``;
});
+engine.registerTag('paginate', Paginate);
engine.plugin(liquidSectionTags());
module.exports = function (content) {
- if (this.cacheable) this.cacheable();
+ if (this.cacheable) this.cacheable();
- engine.options.loaderOptions = loaderUtils.getOptions(this);
- const { isSection } = engine.options.loaderOptions;
+ engine.options.loaderOptions = loaderUtils.getOptions(this);
+ const { isSection } = engine.options.loaderOptions;
- // section handled specially
- if (typeof isSection === 'function' && isSection(this.context)) {
- const sectionName = path.basename(this.resourcePath, '.liquid');
- content = `{% section "${sectionName}" %}`;
- }
+ // section handled specially
+ if (typeof isSection === 'function' && isSection(this.context)) {
+ const sectionName = path.basename(this.resourcePath, '.liquid');
+ content = `{% section "${sectionName}" %}`;
+ }
- const callback = this.async();
+ const callback = this.async();
- return engine
- .parseAndRender(content, engine.options.loaderOptions.globals || {})
- .then((result) => callback(null, result));
+ return engine
+ .parseAndRender(content, engine.options.loaderOptions.globals || {})
+ .then((result) => callback(null, result));
};
diff --git a/shopify-dev-utils/tags/paginate.d.ts b/shopify-dev-utils/tags/paginate.d.ts
new file mode 100644
index 0000000..34abbfe
--- /dev/null
+++ b/shopify-dev-utils/tags/paginate.d.ts
@@ -0,0 +1,2 @@
+import { TagImplOptions } from 'liquidjs/dist/template/tag/tag-impl-options';
+export declare const Paginate: TagImplOptions;
diff --git a/shopify-dev-utils/tags/paginate.js b/shopify-dev-utils/tags/paginate.js
new file mode 100644
index 0000000..f43bcb2
--- /dev/null
+++ b/shopify-dev-utils/tags/paginate.js
@@ -0,0 +1,93 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.Paginate = void 0;
+const liquidjs_1 = require("liquidjs");
+function generatePaginateObj({ offset, perPage, total }) {
+ const pages = Math.ceil(total / perPage);
+ const currentPage = Math.floor((offset + perPage) / perPage);
+ const paginate = {
+ current_offset: offset,
+ current_page: currentPage,
+ items: total,
+ page_size: perPage,
+ parts: Array(pages)
+ .fill(0)
+ .map((_, index) => {
+ const page = index + 1;
+ if (page === currentPage) {
+ return { title: page, is_link: false };
+ }
+ return { title: page, url: `?page=${page}`, is_link: true };
+ }),
+ pages,
+ previous: undefined,
+ next: undefined
+ };
+ if (currentPage === pages && pages > 1) {
+ paginate.previous = {
+ title: '\u0026laquo; Previous',
+ url: `?page=${currentPage - 1}`,
+ is_link: true
+ };
+ }
+ else if (currentPage < pages && pages > 1) {
+ paginate.next = {
+ title: 'Next \u0026raquo;',
+ url: `?page=${currentPage + 1}`,
+ is_link: true
+ };
+ }
+ return paginate;
+}
+function populateVariableObj({ data, depth }) {
+ return depth.reverse().reduce((result, prop) => {
+ return { [prop.getText()]: result };
+ }, data);
+}
+exports.Paginate = {
+ parse: function (tagToken, remainTokens) {
+ this.templates = [];
+ const stream = this.liquid.parser.parseStream(remainTokens);
+ stream
+ .on('start', () => {
+ const toknenizer = new liquidjs_1.Tokenizer(tagToken.args);
+ const list = toknenizer.readValue();
+ const by = toknenizer.readWord();
+ const perPage = toknenizer.readValue();
+ liquidjs_1.assert(list.size() &&
+ by.content === 'by' &&
+ +perPage.getText() > 0 &&
+ +perPage.getText() <= 50, () => `illegal tag: ${tagToken.getText()}`);
+ this.args = { list, perPage: +perPage.getText() };
+ })
+ .on('tag:endpaginate', () => stream.stop())
+ .on('template', (tpl) => {
+ this.templates.push(tpl);
+ })
+ .on('end', () => {
+ throw new Error(`tag ${tagToken.getText()} not closed`);
+ });
+ stream.start();
+ },
+ render: function* (ctx, emitter) {
+ const list = yield liquidjs_1.evalToken(this.args.list, ctx) || [];
+ const perPage = this.args.perPage;
+ const currentPage = +ctx.get(['current_page']);
+ const offset = currentPage ? (currentPage - 1) * perPage : 0;
+ const variableName = this.args.list.getVariableAsText();
+ const scopeList = list.slice(offset, offset + perPage);
+ const data = populateVariableObj({
+ data: scopeList,
+ depth: this.args.list.props
+ });
+ const paginate = generatePaginateObj({
+ offset,
+ perPage,
+ total: list.length
+ });
+ const scope = { [variableName]: data, paginate };
+ ctx.push(scope);
+ yield this.liquid.renderer.renderTemplates(this.templates, ctx, emitter);
+ ctx.pop();
+ }
+};
From 721db1197a767fa9e853b2ca13806bd43a7d27c6 Mon Sep 17 00:00:00 2001
From: Mike
Date: Wed, 25 Nov 2020 16:35:27 -0800
Subject: [PATCH 23/35] collection-list updated
---
src/components/templates/list-collections.liquid | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/templates/list-collections.liquid b/src/components/templates/list-collections.liquid
index 23c9d3f..d5cdcfe 100644
--- a/src/components/templates/list-collections.liquid
+++ b/src/components/templates/list-collections.liquid
@@ -33,4 +33,4 @@
{%- endif -%}
{%- endif -%}
{%- endpaginate -%}
-
\ No newline at end of file
+
From 9893cdaa2c25e235f6dd4e0d1c33391714cfc291 Mon Sep 17 00:00:00 2001
From: Mike
Date: Fri, 27 Nov 2020 19:52:08 -0800
Subject: [PATCH 24/35] collections-list setup pagination
---
src/config/settings_schema.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/config/settings_schema.json b/src/config/settings_schema.json
index d23f356..976b8f7 100644
--- a/src/config/settings_schema.json
+++ b/src/config/settings_schema.json
@@ -58,4 +58,4 @@
}
]
}
-]
\ No newline at end of file
+]
From 3d61230998f92c5f8e7610e13be6cb3356f50d8c Mon Sep 17 00:00:00 2001
From: Mike
Date: Sat, 28 Nov 2020 12:39:13 -0800
Subject: [PATCH 25/35] ESLint added to webpack
---
webpack.config.js | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/webpack.config.js b/webpack.config.js
index 060956b..4ad37b1 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -8,7 +8,6 @@ const WebpackShellPluginNext = require('webpack-shell-plugin-next');
const ESLintPlugin = require('eslint-webpack-plugin');
const { transformLiquid } = require('./shopify-dev-utils/transformLiquid');
-
const isDevMode = argv.mode === 'development';
const stats = isDevMode ? 'errors-warnings' : { children: false };
const port = 9000;
@@ -46,10 +45,13 @@ module.exports = {
options: {
publicPath,
isSection(liquidPath) {
- const diff = path.relative(path.join(__dirname, './src/components/'), liquidPath);
+ const diff = path.relative(
+ path.join(__dirname, './src/components/'),
+ liquidPath
+ );
const componentType = diff.split(path.sep).shift();
return componentType === 'sections';
- }
+ },
},
},
],
@@ -84,7 +86,7 @@ module.exports = {
{
test: /\.js$/,
exclude: /node_modules/,
- use: ["babel-loader"]
+ use: ['babel-loader'],
},
].filter(Boolean),
},
@@ -93,7 +95,7 @@ module.exports = {
cleanStaleWebpackAssets: false,
}),
new ESLintPlugin({
- fix: true
+ fix: true,
}),
isDevMode &&
new WebpackShellPluginNext({
From 6047ad945376e401cbd2806a5b26ea072f903c22 Mon Sep 17 00:00:00 2001
From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com>
Date: Wed, 18 Nov 2020 09:31:28 +0200
Subject: [PATCH 26/35] Finish phase 1, html-hmr
---
liquidDevEntry.js | 55 +++++++++++++++++++++++++++++++++++++++++++++++
webpack.config.js | 4 ++++
2 files changed, 59 insertions(+)
create mode 100644 liquidDevEntry.js
diff --git a/liquidDevEntry.js b/liquidDevEntry.js
new file mode 100644
index 0000000..92f887a
--- /dev/null
+++ b/liquidDevEntry.js
@@ -0,0 +1,55 @@
+const context = require.context('./src', true, /\.liquid$/);
+
+const cache = {};
+
+context.keys().forEach(function (key) {
+ cache[key] = context(key);
+});
+
+function replaceHtml(key, startCommentNode) {
+ const commentNodeType = startCommentNode.nodeType;
+ while (
+ startCommentNode.nextSibling.nodeType !== commentNodeType ||
+ !startCommentNode.nextSibling.nodeValue.includes(`hmr-end: ${key}`)
+ ) {
+ startCommentNode.nextSibling.remove();
+ }
+
+ const tpl = document.createElement('template');
+ tpl.innerHTML = cache[key];
+ startCommentNode.parentNode.insertBefore(tpl.content, startCommentNode.nextSibling);
+}
+
+if (module.hot) {
+ module.hot.accept(context.id, function () {
+ const newContext = require.context('./src', true, /\.liquid$/);
+ const changes = [];
+ newContext.keys().forEach(function (key) {
+ const newFile = newContext(key);
+ if (cache[key] !== newFile) {
+ changes.push(key);
+ cache[key] = newFile;
+ }
+ });
+
+ changes.forEach((changedFile) => {
+ traverseHMRComments(changedFile, replaceHtml);
+ });
+ });
+}
+
+function traverseHMRComments(file, callback) {
+ const nodeIterator = document.createNodeIterator(
+ document.body,
+ NodeFilter.SHOW_COMMENT,
+ function (node) {
+ return node.nodeValue.includes(`hmr-start: ${file}`)
+ ? NodeFilter.FILTER_ACCEPT
+ : NodeFilter.FILTER_REJECT;
+ }
+ );
+
+ while (nodeIterator.nextNode()) {
+ callback(file, nodeIterator.referenceNode);
+ }
+}
diff --git a/webpack.config.js b/webpack.config.js
index 4ad37b1..7d521a9 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -56,6 +56,10 @@ module.exports = {
},
],
},
+ {
+ test: /\.liquid$/,
+ use: ['string-loader'],
+ },
{
test: /\.(sc|sa|c)ss$/,
use: [
From c5978b932b111efe90504e0e857166b6679e8e7a Mon Sep 17 00:00:00 2001
From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com>
Date: Thu, 19 Nov 2020 10:26:13 +0200
Subject: [PATCH 27/35] Add basic liquid process in the pipeline
---
liquidDevEntry.js => liquidDev.entry.js | 4 +-
liquidDev.loader.js | 51 +++++++++++++++++++++++++
webpack.config.js | 8 +++-
3 files changed, 60 insertions(+), 3 deletions(-)
rename liquidDevEntry.js => liquidDev.entry.js (91%)
create mode 100644 liquidDev.loader.js
diff --git a/liquidDevEntry.js b/liquidDev.entry.js
similarity index 91%
rename from liquidDevEntry.js
rename to liquidDev.entry.js
index 92f887a..5276ca8 100644
--- a/liquidDevEntry.js
+++ b/liquidDev.entry.js
@@ -1,4 +1,4 @@
-const context = require.context('./src', true, /\.liquid$/);
+const context = require.context('./src', true, /message\.liquid$/);
const cache = {};
@@ -22,7 +22,7 @@ function replaceHtml(key, startCommentNode) {
if (module.hot) {
module.hot.accept(context.id, function () {
- const newContext = require.context('./src', true, /\.liquid$/);
+ const newContext = require.context('./src', true, /message\.liquid$/);
const changes = [];
newContext.keys().forEach(function (key) {
const newFile = newContext(key);
diff --git a/liquidDev.loader.js b/liquidDev.loader.js
new file mode 100644
index 0000000..6af258e
--- /dev/null
+++ b/liquidDev.loader.js
@@ -0,0 +1,51 @@
+const loaderUtils = require('loader-utils');
+const path = require('path');
+const { Liquid } = require('liquidjs');
+const glob = require('glob');
+const { liquidSectionTags } = require('liquidjs-section-tags');
+
+const liquidFiles = [
+ ...glob
+ .sync('./src/components/**/*.liquid')
+ .map((filePath) => path.resolve(__dirname, path.dirname(filePath)))
+ .reduce((set, dir) => {
+ set.add(dir);
+ return set;
+ }, new Set()),
+];
+
+const engine = new Liquid({
+ root: liquidFiles, // root for layouts/includes lookup
+ extname: '.liquid', // used for layouts/includes, defaults ""
+});
+
+engine.registerFilter('asset_url', function (v) {
+ const { publicPath } = this.context.opts.loaderOptions;
+
+ return `${publicPath}${v}`;
+});
+
+engine.registerFilter('stylesheet_tag', function (v) {
+ return ``; // in Dev mode we load css from js for HMR
+});
+
+engine.registerFilter('script_tag', function (v) {
+ return ``;
+});
+
+engine.plugin(
+ liquidSectionTags({
+ root: liquidFiles,
+ })
+);
+
+module.exports = function (content) {
+ if (this.cacheable) this.cacheable();
+
+ engine.options.loaderOptions = loaderUtils.getOptions(this);
+ const callback = this.async();
+
+ return engine
+ .parseAndRender(content, engine.options.loaderOptions.globals || {})
+ .then((result) => callback(null, result));
+};
diff --git a/webpack.config.js b/webpack.config.js
index 7d521a9..989714c 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -58,7 +58,13 @@ module.exports = {
},
{
test: /\.liquid$/,
- use: ['string-loader'],
+ use: [
+ 'string-loader',
+ {
+ loader: path.resolve(__dirname, 'liquidDev.loader.js'),
+ options: { publicPath },
+ },
+ ],
},
{
test: /\.(sc|sa|c)ss$/,
From f92ad980c2a662a743e72d25a1afdca8575f6fad Mon Sep 17 00:00:00 2001
From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com>
Date: Tue, 24 Nov 2020 00:49:33 +0200
Subject: [PATCH 28/35] Add support for sections & schema tags
---
liquidDev.entry.js | 55 ----------------------------
liquidDev.loader.js | 51 --------------------------
shopify-dev-utils/liquidDev.entry.js | 12 +-----
webpack.config.js | 11 +++++-
4 files changed, 11 insertions(+), 118 deletions(-)
delete mode 100644 liquidDev.entry.js
delete mode 100644 liquidDev.loader.js
diff --git a/liquidDev.entry.js b/liquidDev.entry.js
deleted file mode 100644
index 5276ca8..0000000
--- a/liquidDev.entry.js
+++ /dev/null
@@ -1,55 +0,0 @@
-const context = require.context('./src', true, /message\.liquid$/);
-
-const cache = {};
-
-context.keys().forEach(function (key) {
- cache[key] = context(key);
-});
-
-function replaceHtml(key, startCommentNode) {
- const commentNodeType = startCommentNode.nodeType;
- while (
- startCommentNode.nextSibling.nodeType !== commentNodeType ||
- !startCommentNode.nextSibling.nodeValue.includes(`hmr-end: ${key}`)
- ) {
- startCommentNode.nextSibling.remove();
- }
-
- const tpl = document.createElement('template');
- tpl.innerHTML = cache[key];
- startCommentNode.parentNode.insertBefore(tpl.content, startCommentNode.nextSibling);
-}
-
-if (module.hot) {
- module.hot.accept(context.id, function () {
- const newContext = require.context('./src', true, /message\.liquid$/);
- const changes = [];
- newContext.keys().forEach(function (key) {
- const newFile = newContext(key);
- if (cache[key] !== newFile) {
- changes.push(key);
- cache[key] = newFile;
- }
- });
-
- changes.forEach((changedFile) => {
- traverseHMRComments(changedFile, replaceHtml);
- });
- });
-}
-
-function traverseHMRComments(file, callback) {
- const nodeIterator = document.createNodeIterator(
- document.body,
- NodeFilter.SHOW_COMMENT,
- function (node) {
- return node.nodeValue.includes(`hmr-start: ${file}`)
- ? NodeFilter.FILTER_ACCEPT
- : NodeFilter.FILTER_REJECT;
- }
- );
-
- while (nodeIterator.nextNode()) {
- callback(file, nodeIterator.referenceNode);
- }
-}
diff --git a/liquidDev.loader.js b/liquidDev.loader.js
deleted file mode 100644
index 6af258e..0000000
--- a/liquidDev.loader.js
+++ /dev/null
@@ -1,51 +0,0 @@
-const loaderUtils = require('loader-utils');
-const path = require('path');
-const { Liquid } = require('liquidjs');
-const glob = require('glob');
-const { liquidSectionTags } = require('liquidjs-section-tags');
-
-const liquidFiles = [
- ...glob
- .sync('./src/components/**/*.liquid')
- .map((filePath) => path.resolve(__dirname, path.dirname(filePath)))
- .reduce((set, dir) => {
- set.add(dir);
- return set;
- }, new Set()),
-];
-
-const engine = new Liquid({
- root: liquidFiles, // root for layouts/includes lookup
- extname: '.liquid', // used for layouts/includes, defaults ""
-});
-
-engine.registerFilter('asset_url', function (v) {
- const { publicPath } = this.context.opts.loaderOptions;
-
- return `${publicPath}${v}`;
-});
-
-engine.registerFilter('stylesheet_tag', function (v) {
- return ``; // in Dev mode we load css from js for HMR
-});
-
-engine.registerFilter('script_tag', function (v) {
- return ``;
-});
-
-engine.plugin(
- liquidSectionTags({
- root: liquidFiles,
- })
-);
-
-module.exports = function (content) {
- if (this.cacheable) this.cacheable();
-
- engine.options.loaderOptions = loaderUtils.getOptions(this);
- const callback = this.async();
-
- return engine
- .parseAndRender(content, engine.options.loaderOptions.globals || {})
- .then((result) => callback(null, result));
-};
diff --git a/shopify-dev-utils/liquidDev.entry.js b/shopify-dev-utils/liquidDev.entry.js
index bbbf9ab..a5cdd11 100644
--- a/shopify-dev-utils/liquidDev.entry.js
+++ b/shopify-dev-utils/liquidDev.entry.js
@@ -1,8 +1,4 @@
-const context = require.context(
- '../src',
- true,
- /(collection|footer|featured-product|featured-collection|header|message)\.liquid$/
-);
+const context = require.context('../src', true, /(header|message)\.liquid$/);
const cache = {};
@@ -26,11 +22,7 @@ function replaceHtml(key, startCommentNode) {
if (module.hot) {
module.hot.accept(context.id, function () {
- const newContext = require.context(
- '../src',
- true,
- /(collection|footer|featured-product|featured-collection|header|message)\.liquid$/
- );
+ const newContext = require.context('../src', true, /(header|message)\.liquid$/);
const changes = [];
newContext.keys().forEach(function (key) {
const newFile = newContext(key);
diff --git a/webpack.config.js b/webpack.config.js
index 989714c..450d9c4 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -61,8 +61,15 @@ module.exports = {
use: [
'string-loader',
{
- loader: path.resolve(__dirname, 'liquidDev.loader.js'),
- options: { publicPath },
+ loader: path.resolve(__dirname, 'shopify-dev-utils/liquidDev.loader.js'),
+ options: {
+ publicPath,
+ isSection(liquidPath) {
+ const diff = path.relative(path.join(__dirname, './src/components/'), liquidPath);
+ const componentType = diff.split(path.sep).shift();
+ return componentType === 'sections';
+ }
+ },
},
],
},
From 7a6217c68cb9f750806f940b9cb8bb243288ec62 Mon Sep 17 00:00:00 2001
From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com>
Date: Mon, 7 Dec 2020 18:45:52 +0200
Subject: [PATCH 29/35] Fetch real store data
---
.eslintignore | 2 +
package.json | 5 +-
shopify-dev-utils/liquidDev.entry.js | 12 +++-
shopify-dev-utils/liquidDev.loader.js | 2 +-
shopify-dev-utils/storeData.js | 72 ++++++++++++++--------
shopify-dev-utils/storefrontApi.js | 31 ++++++++++
src/components/sections/product/product.js | 4 +-
src/components/templates/collection.liquid | 2 +-
webpack.config.js | 19 +-----
9 files changed, 99 insertions(+), 50 deletions(-)
create mode 100644 .eslintignore
create mode 100644 shopify-dev-utils/storefrontApi.js
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..db46ba6
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,2 @@
+/dist/**
+/shopify-dev-utils/**
diff --git a/package.json b/package.json
index 197a2f1..738f32e 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"@shopify/eslint-plugin": "^39.0.3",
"@shopify/themekit": "^1.1.6",
"autoprefixer": "^10.0.4",
+ "axios": "^0.21.0",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"babel-plugin-transform-class-properties": "^6.24.1",
@@ -42,8 +43,9 @@
"postcss": "^8.1.10",
"postcss-loader": "^4.0.4",
"prettier": "^2.1.2",
+ "raw-loader": "^4.0.2",
+ "sass": "^1.29.0",
"sass-loader": "^10.1.0",
- "string-loader": "^0.0.1",
"style-loader": "^2.0.0",
"tailwindcss": "^2.0.1",
"transform-class-properties": "^1.0.0-beta",
@@ -52,6 +54,7 @@
"webpack-cli": "^4.2.0",
"webpack-dev-server": "^3.11.0",
"webpack-shell-plugin-next": "^2.0.8",
+ "yaml": "^1.10.0",
"yargs": "^16.1.0"
}
}
diff --git a/shopify-dev-utils/liquidDev.entry.js b/shopify-dev-utils/liquidDev.entry.js
index a5cdd11..bbbf9ab 100644
--- a/shopify-dev-utils/liquidDev.entry.js
+++ b/shopify-dev-utils/liquidDev.entry.js
@@ -1,4 +1,8 @@
-const context = require.context('../src', true, /(header|message)\.liquid$/);
+const context = require.context(
+ '../src',
+ true,
+ /(collection|footer|featured-product|featured-collection|header|message)\.liquid$/
+);
const cache = {};
@@ -22,7 +26,11 @@ function replaceHtml(key, startCommentNode) {
if (module.hot) {
module.hot.accept(context.id, function () {
- const newContext = require.context('../src', true, /(header|message)\.liquid$/);
+ const newContext = require.context(
+ '../src',
+ true,
+ /(collection|footer|featured-product|featured-collection|header|message)\.liquid$/
+ );
const changes = [];
newContext.keys().forEach(function (key) {
const newFile = newContext(key);
diff --git a/shopify-dev-utils/liquidDev.loader.js b/shopify-dev-utils/liquidDev.loader.js
index bfaed8b..046806a 100644
--- a/shopify-dev-utils/liquidDev.loader.js
+++ b/shopify-dev-utils/liquidDev.loader.js
@@ -29,7 +29,7 @@ engine.registerFilter('asset_url', function (v) {
});
engine.registerFilter('stylesheet_tag', function (_v) {
- return ``; // in Dev mode we load css from js for HMR
+ return ''; // in Dev mode we load css from js for HMR
});
engine.registerFilter('script_tag', function (v) {
diff --git a/shopify-dev-utils/storeData.js b/shopify-dev-utils/storeData.js
index 83af7e2..ddb5906 100644
--- a/shopify-dev-utils/storeData.js
+++ b/shopify-dev-utils/storeData.js
@@ -1,26 +1,48 @@
-module.exports.fetchStoreData = function fetchStoreData() {
+const yaml = require('yaml');
+const fs = require('fs');
+const path = require('path');
+const { StorefrontApi } = require('./storefrontApi');
- return {
- 'shop': {
- 'name': 'themekit-webpack-test'
- },
- 'linklists': {
- 'main-menu': {
- 'title': '',
- 'levels': 1,
- 'links': [
- {
- 'title': 'Home',
- 'url': '/',
- 'links': []
- },
- {
- 'title': 'Catalog',
- 'url': '/collections/all',
- 'links': []
- }
- ]
- }
- }
- };
-};
+const configFile = path.join(__dirname, '../config.yml');
+let config = { token: '', baseURL: '' };
+if (fs.existsSync(configFile)) {
+ const configYml = yaml.parse(fs.readFileSync(configFile, 'utf-8'));
+ config.token = configYml.development.storefront_api_key;
+ config.baseURL = configYml.development.store;
+}
+
+async function fetchStoreData() {
+ const storefrontApi = new StorefrontApi(config);
+
+ const { data } = await storefrontApi.getStoreData();
+
+ console.log(JSON.stringify(data, null, 2));
+
+ return {
+ shop: {
+ name: data.shop.name,
+ },
+ linklists: {
+ 'main-menu': {
+ title: '',
+ levels: 1,
+ links: [
+ {
+ title: 'Home',
+ url: '/',
+ links: [],
+ },
+ {
+ title: 'Catalog',
+ url: '/collections/all',
+ links: [],
+ },
+ ],
+ },
+ },
+ };
+}
+
+fetchStoreData();
+
+module.exports.fetchStoreData = fetchStoreData;
diff --git a/shopify-dev-utils/storefrontApi.js b/shopify-dev-utils/storefrontApi.js
new file mode 100644
index 0000000..0d38f30
--- /dev/null
+++ b/shopify-dev-utils/storefrontApi.js
@@ -0,0 +1,31 @@
+const Axios = require('axios');
+
+class StorefrontApi {
+ constructor({ baseURL, token }) {
+ console.log(`https://${baseURL}/api/2020-10/graphql`);
+ this.axios = Axios.create({
+ baseURL: `https://${baseURL}/api/2020-10/graphql`,
+ headers: {
+ Accept: 'application/json',
+ 'Content-Type': 'application/graphql',
+ 'X-Shopify-Storefront-Access-Token': token,
+ },
+ });
+ }
+
+ async getStoreData() {
+ return this.axios
+ .post(
+ '',
+ `
+query {
+ shop {
+ name
+ }
+}`
+ )
+ .then(({ data }) => data);
+ }
+}
+
+module.exports.StorefrontApi = StorefrontApi;
diff --git a/src/components/sections/product/product.js b/src/components/sections/product/product.js
index 0eeabcb..0204c75 100644
--- a/src/components/sections/product/product.js
+++ b/src/components/sections/product/product.js
@@ -1,7 +1,7 @@
import {post} from '../../helpers/cart-fetch-api/cart-fetch-api';
document.getElementById('AddToCartForm').onsubmit = async function (event) {
- const btn = document.getElementById('AddToCartBtn')
+ const btn = document.getElementById('AddToCartBtn');
const id = document.getElementById('AddToCartBtn').value;
const option1 = document.getElementById('option1').value;
const option2 = document.getElementById('option2').value;
@@ -11,7 +11,7 @@ document.getElementById('AddToCartForm').onsubmit = async function (event) {
const response = await post('add.js', {id, option1, option2, qty});
if(response) {
- btn.textContent = 'ITEM ADDED'
+ btn.textContent = 'ITEM ADDED';
}
return false;
};
\ No newline at end of file
diff --git a/src/components/templates/collection.liquid b/src/components/templates/collection.liquid
index 3940917..63a09ae 100644
--- a/src/components/templates/collection.liquid
+++ b/src/components/templates/collection.liquid
@@ -33,4 +33,4 @@
{%- endif -%}
{%- endif -%}
{% endpaginate %}
-
\ No newline at end of file
+
diff --git a/webpack.config.js b/webpack.config.js
index 450d9c4..9b3dad2 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -39,7 +39,7 @@ module.exports = {
isDevMode && {
test: /\.liquid$/,
use: [
- 'string-loader',
+ { loader: 'raw-loader', options: { esModule: false } },
{
loader: path.resolve(__dirname, 'shopify-dev-utils/liquidDev.loader.js'),
options: {
@@ -56,23 +56,6 @@ module.exports = {
},
],
},
- {
- test: /\.liquid$/,
- use: [
- 'string-loader',
- {
- loader: path.resolve(__dirname, 'shopify-dev-utils/liquidDev.loader.js'),
- options: {
- publicPath,
- isSection(liquidPath) {
- const diff = path.relative(path.join(__dirname, './src/components/'), liquidPath);
- const componentType = diff.split(path.sep).shift();
- return componentType === 'sections';
- }
- },
- },
- ],
- },
{
test: /\.(sc|sa|c)ss$/,
use: [
From 990840bdc785425d503c32b3974d19093cd35991 Mon Sep 17 00:00:00 2001
From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com>
Date: Wed, 9 Dec 2020 11:32:21 +0200
Subject: [PATCH 30/35] Fix paginate bugs
---
shopify-dev-utils/tags/paginate.js | 21 ++++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/shopify-dev-utils/tags/paginate.js b/shopify-dev-utils/tags/paginate.js
index f43bcb2..a1730fc 100644
--- a/shopify-dev-utils/tags/paginate.js
+++ b/shopify-dev-utils/tags/paginate.js
@@ -39,10 +39,19 @@ function generatePaginateObj({ offset, perPage, total }) {
}
return paginate;
}
-function populateVariableObj({ data, depth }) {
- return depth.reverse().reduce((result, prop) => {
- return { [prop.getText()]: result };
- }, data);
+function populateVariableObj({ list, originalValue, depth }) {
+ if (depth.length === 0) {
+ return list;
+ }
+ const clone = JSON.parse(JSON.stringify(originalValue));
+ depth.reduce((result, prop, index) => {
+ const propName = prop.getText();
+ if (index === depth.length - 1) {
+ result[propName] = list;
+ }
+ return result[propName] || {};
+ }, clone);
+ return clone;
}
exports.Paginate = {
parse: function (tagToken, remainTokens) {
@@ -75,9 +84,11 @@ exports.Paginate = {
const currentPage = +ctx.get(['current_page']);
const offset = currentPage ? (currentPage - 1) * perPage : 0;
const variableName = this.args.list.getVariableAsText();
+ const originalValue = ctx.get([variableName]);
const scopeList = list.slice(offset, offset + perPage);
const data = populateVariableObj({
- data: scopeList,
+ list: scopeList,
+ originalValue,
depth: this.args.list.props
});
const paginate = generatePaginateObj({
From 6db8f4f5788dcf99c0402cb2d1f3a0ff8a5ec9b9 Mon Sep 17 00:00:00 2001
From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com>
Date: Wed, 9 Dec 2020 11:34:00 +0200
Subject: [PATCH 31/35] Add store front data fetcher
---
.../convertToGlobalDataStructure.js | 33 +++++++
shopify-dev-utils/liquidDev.loader.js | 90 +++++++++++--------
shopify-dev-utils/storeData.js | 14 +--
shopify-dev-utils/storefrontApi.js | 52 ++++++++++-
src/components/templates/collection.liquid | 21 +++--
5 files changed, 156 insertions(+), 54 deletions(-)
create mode 100644 shopify-dev-utils/convertToGlobalDataStructure.js
diff --git a/shopify-dev-utils/convertToGlobalDataStructure.js b/shopify-dev-utils/convertToGlobalDataStructure.js
new file mode 100644
index 0000000..9c557b1
--- /dev/null
+++ b/shopify-dev-utils/convertToGlobalDataStructure.js
@@ -0,0 +1,33 @@
+module.exports.convertToGlobalDataStructure = function convertToGlobalDataStructure(gqlData) {
+ // return gqlData;
+ return {
+ shop: {
+ name: gqlData.shop.name,
+ },
+ collections: gqlData.collections.edges.map(({ node }) => ({
+ title: node.title,
+ id: node.id,
+ handle: node.handle,
+ image: node.image,
+ description: node.description,
+ url: 'unknown-url', // TODO: find a way to get the collection url
+ products: node.products.edges.map((product) => ({
+ id: product.node.id,
+ title: product.node.title,
+ description: product.node.description,
+ handle: product.node.handle,
+ available: product.node.availableForSale,
+ price: product.node.priceRange,
+ url: 'unknown-url', // TODO: find a way to get the product url
+ featured_image:
+ product.node.images.edges.length > 0
+ ? {
+ id: product.node.images.edges[0].node.id,
+ alt: product.node.images.edges[0].node.altText,
+ src: product.node.images.edges[0].node.originalSrc,
+ }
+ : null,
+ })),
+ })),
+ };
+};
diff --git a/shopify-dev-utils/liquidDev.loader.js b/shopify-dev-utils/liquidDev.loader.js
index 046806a..d99ae17 100644
--- a/shopify-dev-utils/liquidDev.loader.js
+++ b/shopify-dev-utils/liquidDev.loader.js
@@ -6,41 +6,61 @@ const { fetchStoreData } = require('./storeData');
const { liquidSectionTags } = require('./section-tags/index');
const { Paginate } = require('./tags/paginate');
-const liquidFiles = [
- ...glob
- .sync('./src/components/**/*.liquid')
- .map((filePath) => path.resolve(path.join(__dirname, '../'), path.dirname(filePath)))
- .reduce((set, dir) => {
- set.add(dir);
- return set;
- }, new Set()),
-];
-
-const engine = new Liquid({
- root: liquidFiles, // root for layouts/includes lookup
- extname: '.liquid', // used for layouts/includes, defaults "",
- globals: fetchStoreData(),
-});
-
-engine.registerFilter('asset_url', function (v) {
- const { publicPath } = this.context.opts.loaderOptions;
-
- return `${publicPath}${v}`;
-});
-
-engine.registerFilter('stylesheet_tag', function (_v) {
- return ''; // in Dev mode we load css from js for HMR
-});
-
-engine.registerFilter('script_tag', function (v) {
- return ``;
-});
-
-engine.registerTag('paginate', Paginate);
-engine.plugin(liquidSectionTags());
-
-module.exports = function (content) {
+let engine;
+let loadPromise;
+
+function initEngine() {
+ if (!loadPromise) {
+ loadPromise = new Promise(async (resolve) => {
+ const liquidFiles = [
+ ...glob
+ .sync('./src/components/**/*.liquid')
+ .map((filePath) =>
+ path.resolve(path.join(__dirname, '../'), path.dirname(filePath))
+ )
+ .reduce((set, dir) => {
+ set.add(dir);
+ return set;
+ }, new Set()),
+ ];
+
+ engine = new Liquid({
+ root: liquidFiles, // root for layouts/includes lookup
+ extname: '.liquid', // used for layouts/includes, defaults "",
+ globals: await fetchStoreData(),
+ });
+
+ engine.registerFilter('asset_url', function (v) {
+ const { publicPath } = this.context.opts.loaderOptions;
+
+ return `${publicPath}${v}`;
+ });
+
+ engine.registerFilter('stylesheet_tag', function (_v) {
+ return ''; // in Dev mode we load css from js for HMR
+ });
+
+ engine.registerFilter('script_tag', function (v) {
+ return ``;
+ });
+
+ engine.registerTag('paginate', Paginate);
+ engine.plugin(liquidSectionTags());
+
+ resolve();
+ });
+ }
+
+ return loadPromise;
+}
+
+module.exports = async function (content) {
if (this.cacheable) this.cacheable();
+ const callback = this.async();
+
+ if (!engine) {
+ await initEngine();
+ }
engine.options.loaderOptions = loaderUtils.getOptions(this);
const { isSection } = engine.options.loaderOptions;
@@ -51,8 +71,6 @@ module.exports = function (content) {
content = `{% section "${sectionName}" %}`;
}
- const callback = this.async();
-
return engine
.parseAndRender(content, engine.options.loaderOptions.globals || {})
.then((result) => callback(null, result));
diff --git a/shopify-dev-utils/storeData.js b/shopify-dev-utils/storeData.js
index ddb5906..9189779 100644
--- a/shopify-dev-utils/storeData.js
+++ b/shopify-dev-utils/storeData.js
@@ -1,6 +1,7 @@
const yaml = require('yaml');
const fs = require('fs');
const path = require('path');
+const { convertToGlobalDataStructure } = require('./convertToGlobalDataStructure');
const { StorefrontApi } = require('./storefrontApi');
const configFile = path.join(__dirname, '../config.yml');
@@ -9,14 +10,18 @@ if (fs.existsSync(configFile)) {
const configYml = yaml.parse(fs.readFileSync(configFile, 'utf-8'));
config.token = configYml.development.storefront_api_key;
config.baseURL = configYml.development.store;
+
+ if (!config.token) {
+ console.warn(`'storefront_api_key' was not found in 'config.yml'`);
+ }
}
async function fetchStoreData() {
const storefrontApi = new StorefrontApi(config);
- const { data } = await storefrontApi.getStoreData();
-
- console.log(JSON.stringify(data, null, 2));
+ const data = await storefrontApi
+ .getStoreData()
+ .then(({ data }) => convertToGlobalDataStructure(data));
return {
shop: {
@@ -40,9 +45,8 @@ async function fetchStoreData() {
],
},
},
+ collection: data.collections[0],
};
}
-fetchStoreData();
-
module.exports.fetchStoreData = fetchStoreData;
diff --git a/shopify-dev-utils/storefrontApi.js b/shopify-dev-utils/storefrontApi.js
index 0d38f30..43febaf 100644
--- a/shopify-dev-utils/storefrontApi.js
+++ b/shopify-dev-utils/storefrontApi.js
@@ -18,11 +18,59 @@ class StorefrontApi {
.post(
'',
`
-query {
+{
shop {
name
}
-}`
+ collections(first: 50) {
+ edges {
+ node {
+ id
+ title
+ handle
+ description
+ image(scale:1) {
+ id
+ altText
+ originalSrc
+ transformedSrc
+ }
+ products(first: 50) {
+ edges {
+ node {
+ id
+ title
+ description
+ handle
+ availableForSale
+ priceRange {
+ maxVariantPrice {
+ amount
+ currencyCode
+ }
+ minVariantPrice {
+ amount
+ currencyCode
+ }
+ }
+ images(first: 1) {
+ edges {
+ node {
+ id
+ altText
+ originalSrc
+ }
+ }
+ }
+ onlineStoreUrl
+ }
+ }
+ }
+ }
+ }
+ }
+}
+`
)
.then(({ data }) => data);
}
diff --git a/src/components/templates/collection.liquid b/src/components/templates/collection.liquid
index 63a09ae..5643b18 100644
--- a/src/components/templates/collection.liquid
+++ b/src/components/templates/collection.liquid
@@ -1,16 +1,16 @@
{% paginate collection.products by 8 %}
- {{ collection.title }}
-
+
{{ collection.title }}
+
{% for product in collection.products %}
{{ product.title }}
-
{{ product.content | truncate: 40 }}
+
{{ product.description | strip_html | truncate: 40 }}

+ alt="{{ product.featured_image.alt | escape }}" />
{{ product.price | money_without_trailing_zeros }}