Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
0aea4ba
Finish phase 1, html-hmr
felixmosh Nov 18, 2020
2ad0fb4
Finish phase 1, html-hmr
felixmosh Nov 18, 2020
ce63646
Add basic liquid process in the pipeline
felixmosh Nov 19, 2020
2210fde
Merge remote-tracking branch 'origin/liquid-hmr' into liquid-hmr
felixmosh Nov 21, 2020
b17b94f
Add support for sections & schema tags
felixmosh Nov 23, 2020
6d0ff6c
Add static store data
felixmosh Nov 24, 2020
dc4b5ae
Add support for paginate tag
felixmosh Nov 28, 2020
a4b1864
collection-list updated
mike-diff Nov 26, 2020
0c90c67
collections-list setup pagination
mike-diff Nov 28, 2020
42c68da
removed infiniate scroll for now
mike-diff Nov 28, 2020
46607d3
product template updated
mike-diff Nov 28, 2020
b16c84e
ESLint added to webpack
mike-diff Nov 28, 2020
5799b7c
Create CONTRIBUTING.md
mike-diff Nov 28, 2020
657b80e
Create CODE_OF_CONDUCT.md
mike-diff Nov 28, 2020
fa8d3b1
added refs to conduct
mike-diff Nov 28, 2020
8c46638
Finish phase 1, html-hmr
felixmosh Nov 18, 2020
62f754b
Add basic liquid process in the pipeline
felixmosh Nov 19, 2020
932e5e3
Add support for sections & schema tags
felixmosh Nov 23, 2020
adbc490
Merge branch 'master' into liquid-hmr
felixmosh Nov 29, 2020
dd4966a
Finish phase 1, html-hmr
felixmosh Nov 18, 2020
7ce4b0b
Add basic liquid process in the pipeline
felixmosh Nov 19, 2020
289ba43
Add support for sections & schema tags
felixmosh Nov 23, 2020
d2533c0
Add static store data
felixmosh Nov 24, 2020
5ff723d
Add support for paginate tag
felixmosh Nov 28, 2020
721db11
collection-list updated
mike-diff Nov 26, 2020
9893cda
collections-list setup pagination
mike-diff Nov 28, 2020
3d61230
ESLint added to webpack
mike-diff Nov 28, 2020
6047ad9
Finish phase 1, html-hmr
felixmosh Nov 18, 2020
c5978b9
Add basic liquid process in the pipeline
felixmosh Nov 19, 2020
f92ad98
Add support for sections & schema tags
felixmosh Nov 23, 2020
2722e55
Merge remote-tracking branch 'origin/liquid-hmr' into liquid-hmr
felixmosh Dec 5, 2020
7a6217c
Fetch real store data
felixmosh Dec 7, 2020
990840b
Fix paginate bugs
felixmosh Dec 9, 2020
6db8f4f
Add store front data fetcher
felixmosh Dec 9, 2020
3726a93
Add default_pagination filter
felixmosh Dec 25, 2020
8e9a58d
Add global settings from settings_schema file
felixmosh Dec 25, 2020
cdcd35f
Add within filter
felixmosh Dec 25, 2020
3c229d9
Add money filters
felixmosh Dec 25, 2020
f3f8b37
Merge remote-tracking branch 'upstream/master' into liquid-hmr
felixmosh Jan 25, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/dist/**
/shopify-dev-utils/**
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,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",
Expand All @@ -36,12 +37,15 @@
"file-loader": "^6.0.0",
"glob": "^7.1.6",
"html-webpack-plugin": "^4.3.0",
"liquidjs": "^9.16.1",
"mini-css-extract-plugin": "^1.3.1",
"node-fetch": "^2.6.1",
"node-sass": "^5.0.0",
"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",
"style-loader": "^2.0.0",
"tailwindcss": "^2.0.1",
Expand All @@ -52,6 +56,7 @@
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^5.7.3",
"webpack-shell-plugin-next": "^2.0.8",
"yaml": "^1.10.0",
"yargs": "^16.1.0"
}
}
38 changes: 38 additions & 0 deletions shopify-dev-utils/convertToGlobalDataStructure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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: `/collections/${node.handle}`,
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.maxVariantPrice, // preserve the entire obj for money-* filters
price_max: product.node.priceRange.maxVariantPrice,
price_min: product.node.priceRange.minVariantPrice,
price_varies:
+product.node.priceRange.maxVariantPrice.amount !==
+product.node.priceRange.minVariantPrice,
url: `/products/${product.node.handle}`,
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,
})),
})),
};
};
5 changes: 5 additions & 0 deletions shopify-dev-utils/filters/asset_url.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports.assetUrl = function assetUrl(v) {
const { publicPath } = this.context.opts.loaderOptions;

return `${publicPath}${v}`;
};
26 changes: 26 additions & 0 deletions shopify-dev-utils/filters/default_pagination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module.exports.defaultPagination = function defaultPagination(paginate, ...rest) {
const next = rest.filter((arg) => arg[0] === 'next').pop();
const previous = rest.filter((arg) => arg[0] === 'previous').pop();

const prevLabel =
previous.length > 0 ? previous.pop() : paginate.previous ? paginate.previous.title : '';
const nextLabel = next.length > 0 ? next.pop() : paginate.next ? paginate.next.title : '';

const prevPart = paginate.previous
? `<span class="prev"><a href="${paginate.previous.url}" title="${prevLabel}">${prevLabel}</a></span>`
: '';
const nextPart = paginate.next
? `<span class="next"><a href="${paginate.next.url}" title="${nextLabel}">${nextLabel}</a></span>`
: '';

const pagesPart = paginate.parts
.map((part) => {
if (part.is_link) {
return `<span class="page"><a href="${part.url}" title="${part.title}">${part.title}</a></span>`;
}
return `<span class="page current">${part.title}</span>`;
})
.join('');

return `${prevPart}${pagesPart}${nextPart}`;
};
13 changes: 13 additions & 0 deletions shopify-dev-utils/filters/money_with_currency.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module.exports.moneyWithCurrency = function moneyWithCurrency(price) {
if (!price || !price.currencyCode || !price.amount) {
return '';
}

// the price that this object gets has 2 fields it is not the same value in "real" env,
// at real it should be only a number multiplied by 100
return new Intl.NumberFormat('en', {
style: 'currency',
currency: price.currencyCode,
maximumFractionDigits: 2,
}).format(price.amount);
};
14 changes: 14 additions & 0 deletions shopify-dev-utils/filters/money_without_trailing_zeros.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const { moneyWithCurrency } = require('./money_with_currency');

module.exports.moneyWithoutTrailingZeros = function moneyWithoutTrailingZeros(price) {
// the price that this object gets has 2 fields it is not the same value in "real" env,
// at real it should be only a number multiplied by 100
const moneyWithCurrencyAndTrailingZeros = moneyWithCurrency(price);
return moneyWithCurrencyAndTrailingZeros.replace(
/([,.][^0]*(0+))\D*$/,
(match, group, zeros) => {
const cutSize = zeros.length > 1 ? zeros.length + 1 : zeros.length;
return match.replace(group, group.substring(0, group.length - cutSize));
}
);
};
3 changes: 3 additions & 0 deletions shopify-dev-utils/filters/script_tag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports.scriptTag = function scriptTag(v) {
return `<script src="${v}"></script>`;
};
3 changes: 3 additions & 0 deletions shopify-dev-utils/filters/stylesheet_tag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports.stylesheetTag = function stylesheetTag() {
return ''; // in Dev mode we load css from js for HMR
};
3 changes: 3 additions & 0 deletions shopify-dev-utils/filters/within.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports.within = function within(productUrl, collection) {
return `${collection.url}${productUrl}`;
};
63 changes: 63 additions & 0 deletions shopify-dev-utils/liquidDev.entry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const context = require.context(
'../src',
true,
/(collection|footer|featured-product|featured-collection|header|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,
/(collection|footer|featured-product|featured-collection|header|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);
}
}
78 changes: 78 additions & 0 deletions shopify-dev-utils/liquidDev.loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
const loaderUtils = require('loader-utils');
const path = require('path');
const { Liquid } = require('liquidjs');
const glob = require('glob');
const { moneyWithoutTrailingZeros } = require('./filters/money_without_trailing_zeros');
const { moneyWithCurrency } = require('./filters/money_with_currency');
const { within } = require('./filters/within');
const { defaultPagination } = require('./filters/default_pagination');
const { scriptTag } = require('./filters/script_tag');
const { stylesheetTag } = require('./filters/stylesheet_tag');
const { assetUrl } = require('./filters/asset_url');
const { getStoreGlobalData } = require('./storeData');
const { liquidSectionTags } = require('./section-tags/index');
const { Paginate } = require('./tags/paginate');

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 getStoreGlobalData(),
});

engine.registerFilter('asset_url', assetUrl);
engine.registerFilter('stylesheet_tag', stylesheetTag);
engine.registerFilter('script_tag', scriptTag);
engine.registerFilter('default_pagination', defaultPagination);
engine.registerFilter('within', within);
engine.registerFilter('money_with_currency', moneyWithCurrency);
engine.registerFilter('money_without_trailing_zeros', moneyWithoutTrailingZeros);

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;

// section handled specially
if (typeof isSection === 'function' && isSection(this.context)) {
const sectionName = path.basename(this.resourcePath, '.liquid');
content = `{% section "${sectionName}" %}`;
}

return engine
.parseAndRender(content, engine.options.loaderOptions.globals || {})
.then((result) => callback(null, result));
};
2 changes: 2 additions & 0 deletions shopify-dev-utils/section-tags/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { Liquid } from 'liquidjs';
export declare function liquidSectionTags(): (this: Liquid) => void;
16 changes: 16 additions & 0 deletions shopify-dev-utils/section-tags/index.js
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 2 additions & 0 deletions shopify-dev-utils/section-tags/javascript.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { TagImplOptions } from 'liquidjs/dist/template/tag/tag-impl-options';
export declare const JavaScript: TagImplOptions;
24 changes: 24 additions & 0 deletions shopify-dev-utils/section-tags/javascript.js
Original file line number Diff line number Diff line change
@@ -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 `<script>${text}</script>`;
}
};
2 changes: 2 additions & 0 deletions shopify-dev-utils/section-tags/schema.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { TagImplOptions } from 'liquidjs/dist/template/tag/tag-impl-options';
export declare const Schema: TagImplOptions;
45 changes: 45 additions & 0 deletions shopify-dev-utils/section-tags/schema.js
Original file line number Diff line number Diff line change
@@ -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 '';
}
};
2 changes: 2 additions & 0 deletions shopify-dev-utils/section-tags/section.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { TagImplOptions } from 'liquidjs';
export declare const Section: TagImplOptions;
Loading