@softlimit/theme-envy 0.1.2-alpha → 0.1.4-alpha
Sign up to get free protection for your applications and to get access to all the features.
- package/.github/workflows/release-please.yml +1 -3
- package/README.md +1 -1
- package/build/functions/get-all.js +34 -15
- package/build/functions/index.js +0 -1
- package/build/functions/liquid/functions/list-dependencies.js +1 -1
- package/build/functions/liquid/functions/section-schema-inject.js +44 -4
- package/build/functions/parent-theme/index.js +77 -0
- package/build/functions/parent-theme/parent-theme-files.js +47 -0
- package/build/functions/webpack-plugins/retry-chunk-load-plugin.js +88 -0
- package/build/functions/webpack.js +24 -1
- package/build/index.js +3 -4
- package/build/requires/assets.js +2 -3
- package/build/requires/globals/index.js +0 -1
- package/build/requires/globals/prepare-install-hooks-schema.js +1 -2
- package/build/requires/globals/progress-bar.js +1 -2
- package/build/requires/globals/theme-require.js +11 -9
- package/build/requires/locales.js +13 -8
- package/build/requires/scripts/script-builders/elements.build.js +29 -21
- package/build/requires/styles/styles-builders/theme-envy.css.js +5 -1
- package/build/theme-envy.config.js +18 -7
- package/helpers/functions/parse-schema.js +20 -0
- package/helpers/functions/pull-json.js +1 -1
- package/helpers/index.js +1 -0
- package/index.js +1 -0
- package/init/functions/copy-starter-config-files/configs/postcss.config.js +7 -7
- package/init/functions/copy-starter-config-files/configs/tailwind.config.js +18 -4
- package/init/functions/copy-starter-config-files/configs/theme.config.js +1 -1
- package/package.json +5 -5
- package/build/functions/parent-theme-files.js +0 -25
- package/build/functions/tailwind.js +0 -31
- package/build/requires/globals/parent-theme.js +0 -28
- package/build/requires/styles/tailwind-base.css +0 -3
package/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
<!-- omit in toc -->
|
5
5
|
## _The lean, mean development machine for your Shopify themes_
|
6
6
|
|
7
|
-
[
|
7
|
+
[<img src="https://cdn.shopify.com/s/files/1/0581/4760/2587/files/Built_by_Softlimit_-_Orange.png?v=1681137186" width="140px" alt="Built by Softlimit">](https://www.softlimit.com/)
|
8
8
|
|
9
9
|
Theme Envy is a feature-rich, performance-optimized Shopify theme development environment. With the extended liquid tools, you can move beyond liquid snippets with repeatable code. Theme Envy compiles everything for you using a combination of custom build processes, Node, Webpack, and Tailwind.
|
10
10
|
<!-- omit in toc -->
|
@@ -11,69 +11,87 @@
|
|
11
11
|
*/
|
12
12
|
const path = require('path')
|
13
13
|
const glob = require('glob')
|
14
|
-
const parentThemeFiles = require('./parent-theme-files')
|
14
|
+
const parentThemeFiles = require('./parent-theme/parent-theme-files')
|
15
|
+
|
16
|
+
// ignore node_modules in globs
|
17
|
+
const globSync = (src, pattern) => glob.sync(path.resolve(src, pattern), {
|
18
|
+
ignore: {
|
19
|
+
ignored: p => {
|
20
|
+
// ignore node_modules in parent theme but not the parent theme itself (within node_modules)
|
21
|
+
if (ThemeEnvy.parentTheme) {
|
22
|
+
return p.fullpath().includes(path.resolve(ThemeEnvy.parentTheme.path, 'node_modules')) ? true : p.fullpath().includes('node_modules') && !p.fullpath().includes(ThemeEnvy.parentTheme.path)
|
23
|
+
}
|
24
|
+
return p.fullpath().includes('node_modules')
|
25
|
+
},
|
26
|
+
}
|
27
|
+
})
|
15
28
|
|
16
29
|
const globs = {
|
17
30
|
assets: {
|
18
31
|
glob(src) {
|
19
|
-
return
|
32
|
+
return globSync(src, '**/assets/**/*')
|
20
33
|
},
|
21
34
|
},
|
22
35
|
config: {
|
23
36
|
glob(src) {
|
24
|
-
return
|
37
|
+
return globSync(src, '**/config/**/*.js')
|
25
38
|
},
|
26
39
|
filter: file => path.basename(file) !== 'settings_schema.js',
|
27
40
|
},
|
28
41
|
criticalCSS: {
|
29
42
|
glob(src) {
|
30
|
-
return
|
43
|
+
return globSync(src, '**/critical.css')
|
31
44
|
},
|
32
45
|
},
|
33
46
|
elements: {
|
34
47
|
glob(src) {
|
35
|
-
return [...
|
48
|
+
return [...globSync(src, '**/elements/**/index.js'), ...globSync(src, '**/elements/*.js')]
|
36
49
|
}
|
37
50
|
},
|
38
51
|
features: {
|
39
52
|
glob(src) {
|
40
|
-
return
|
53
|
+
return globSync(src, '**/features/**/index.js')
|
41
54
|
},
|
42
55
|
},
|
43
56
|
installs: {
|
44
57
|
glob(src) {
|
45
|
-
return
|
58
|
+
return globSync(src, '**/install.js')
|
46
59
|
},
|
47
60
|
},
|
48
61
|
liquid: {
|
49
62
|
glob(src) {
|
50
|
-
return
|
63
|
+
return globSync(src, '**/*.liquid')
|
51
64
|
},
|
52
65
|
filter: file => !file.includes('partials'),
|
53
66
|
},
|
67
|
+
locales: {
|
68
|
+
glob(src) {
|
69
|
+
return globSync(src, '**/locales/**/*.json')
|
70
|
+
}
|
71
|
+
},
|
54
72
|
partials: {
|
55
73
|
glob(src) {
|
56
|
-
return
|
74
|
+
return globSync(src, '**/partials/**/*.liquid')
|
57
75
|
},
|
58
76
|
},
|
59
77
|
schema: {
|
60
78
|
glob(src) {
|
61
|
-
return
|
79
|
+
return globSync(src, '**/schema/**/*.js')
|
62
80
|
},
|
63
81
|
},
|
64
82
|
sectionGroups: {
|
65
83
|
glob(src) {
|
66
|
-
return
|
84
|
+
return globSync(src, '**/sections/**/*.json')
|
67
85
|
},
|
68
86
|
},
|
69
87
|
snippets: {
|
70
88
|
glob(src) {
|
71
|
-
return
|
89
|
+
return globSync(src, '**/snippets/**/*.liquid')
|
72
90
|
},
|
73
91
|
},
|
74
92
|
templates: {
|
75
93
|
glob(src) {
|
76
|
-
return
|
94
|
+
return globSync(src, '**/templates/**/*.json')
|
77
95
|
},
|
78
96
|
},
|
79
97
|
}
|
@@ -81,16 +99,17 @@ const globs = {
|
|
81
99
|
module.exports = function(type) {
|
82
100
|
function getFiles(src, only) {
|
83
101
|
// src is either the themePath or the parentTheme
|
84
|
-
// only is a list of directory
|
102
|
+
// only is a list of directory paths to filter against, used for parentTheme
|
85
103
|
let files = globs[type].glob(src)
|
86
104
|
if (only) {
|
87
105
|
files = files.filter(file => {
|
88
|
-
return only.some(dir => file.
|
106
|
+
return only.some(dir => file.includes(`${dir}/`) || file.includes(`${dir}.js`))
|
89
107
|
})
|
90
108
|
}
|
91
109
|
if (globs[type].filter) {
|
92
110
|
files = files.filter(globs[type].filter)
|
93
111
|
}
|
112
|
+
|
94
113
|
return files
|
95
114
|
}
|
96
115
|
const files = getFiles(ThemeEnvy.themePath)
|
package/build/functions/index.js
CHANGED
@@ -22,7 +22,7 @@ const listDependencies = ({ filePath, source }) => {
|
|
22
22
|
const action = tag[2]
|
23
23
|
const name = tag[3]
|
24
24
|
if (action === 'partial') {
|
25
|
-
const file = globbedPartials.filter(partial =>
|
25
|
+
const file = globbedPartials.filter(partial => path.basename(partial) === `${name}.liquid`)
|
26
26
|
// if partialPath doesn't return anything, exit process and output error
|
27
27
|
if (file.length === 0) {
|
28
28
|
console.log(`\n${logSymbols.error} ${chalk.red.bold('Error:')}\n\n${chalk.red(`${name}.liquid`)} partial file not found, referenced in:\n${chalk.dim.underline(filePath)}\n\nTo resolve, confirm the partial file exists and that the file\nname reference in the {% partial %} tag matches the partial file.\n`)
|
@@ -1,21 +1,61 @@
|
|
1
1
|
/**
|
2
2
|
* @file Prebuild helper script that will inject the schema js file into the corresponding sections/*.liquid file
|
3
|
+
* also injects installs for blocks/section schema
|
3
4
|
* @example
|
4
5
|
* // include the schema in the section liquid file
|
5
6
|
* {% schema 'schema-file.js' %}
|
6
7
|
*/
|
8
|
+
const path = require('path')
|
9
|
+
const { parseSchema } = require('#Helpers')
|
7
10
|
|
8
11
|
module.exports = function({ source, filePath }) {
|
9
12
|
if (!filePath.includes('sections')) return source
|
10
|
-
|
11
|
-
|
12
|
-
if (
|
13
|
+
// inject installs into inlined schema with {% schema %} {% endschema %} tags
|
14
|
+
const hasSchemaTag = source.match(/{% schema %}/g)
|
15
|
+
if (hasSchemaTag) return injectInstallsSchema({ source, filePath })
|
16
|
+
// inject schema from file into schema with {% schema 'filename.js' %} tags
|
17
|
+
const hasJsSchema = source.match(/{% schema '(.*)' %}/g) || source.match(/{% schema "(.*)" %}/g)
|
18
|
+
if (hasJsSchema) return injectJsSchema({ source, filePath, schema: hasJsSchema })
|
19
|
+
// if neither of the above...
|
20
|
+
return source
|
21
|
+
}
|
22
|
+
|
23
|
+
function injectJsSchema({ source, filePath, schema }) {
|
13
24
|
// regexp for a quoted string within our schema match
|
14
25
|
const schemaFile = schema[0].match(/'(.*)'/)[1] || schema[0].match(/"(.*)"/)[1]
|
15
26
|
// load the file export
|
16
27
|
const schemaSource = ThemeRequire(schemaFile, { loader: filePath })
|
28
|
+
// check for installs
|
29
|
+
const schemaWithInjections = checkInstalls({ schema: schemaSource, filePath })
|
17
30
|
// replace the {% schema %} tag with the schema string and update asset
|
18
|
-
return source.replace(schema[0], formatSchema(
|
31
|
+
return source.replace(schema[0], formatSchema(schemaWithInjections))
|
32
|
+
}
|
33
|
+
|
34
|
+
function injectInstallsSchema({ source, filePath }) {
|
35
|
+
// split our schema tag from the rest of the file
|
36
|
+
const { schema, schemaStart, schemaEnd } = parseSchema(source)
|
37
|
+
const schemaWithInjections = checkInstalls({ schema, filePath })
|
38
|
+
// replace everything between schemaStart and schemaEnd with the new schema
|
39
|
+
source = source.replace(source.substring(schemaStart, schemaEnd + String('{% endschema %}').length), formatSchema(schemaWithInjections))
|
40
|
+
return source
|
41
|
+
}
|
42
|
+
|
43
|
+
function checkInstalls({ schema, filePath }) {
|
44
|
+
// check our schema from install.js files and inject into schema
|
45
|
+
const sectionName = `${path.basename(path.dirname(filePath))}/${path.basename(filePath)}`
|
46
|
+
if (!ThemeEnvy.schema || !ThemeEnvy.schema[sectionName]) return schema
|
47
|
+
|
48
|
+
if (ThemeEnvy.schema[sectionName].settings) {
|
49
|
+
schema.settings = schema.settings || []
|
50
|
+
schema.settings = [...schema.settings, ...ThemeEnvy.schema[sectionName].settings]
|
51
|
+
}
|
52
|
+
|
53
|
+
if (ThemeEnvy.schema[sectionName].blocks) {
|
54
|
+
schema.blocks = schema.blocks || []
|
55
|
+
schema.blocks = [...schema.blocks, ...ThemeEnvy.schema[sectionName].blocks]
|
56
|
+
}
|
57
|
+
|
58
|
+
return schema
|
19
59
|
}
|
20
60
|
|
21
61
|
function formatSchema(schema) {
|
@@ -0,0 +1,77 @@
|
|
1
|
+
/**
|
2
|
+
* @private
|
3
|
+
* @file Checks for a parent theme in node_modules or relative path and sets ThemeEnvy.parentTheme to the path
|
4
|
+
*/
|
5
|
+
|
6
|
+
const fs = require('fs-extra')
|
7
|
+
const path = require('path');
|
8
|
+
|
9
|
+
(() => {
|
10
|
+
const ThemeConfigPath = path.resolve(process.cwd(), 'theme.config.js')
|
11
|
+
if (!fs.existsSync(ThemeConfigPath)) return
|
12
|
+
ThemeEnvy.childPreferred = ThemeEnvy.childPreferred || []
|
13
|
+
|
14
|
+
// look for parent theme in node_modules
|
15
|
+
const parentThemePath = ThemeEnvy.parentTheme?.path
|
16
|
+
if (!parentThemePath) {
|
17
|
+
removeParentThemeReference()
|
18
|
+
return
|
19
|
+
}
|
20
|
+
const parentThemePkg = fs.existsSync(path.resolve(process.cwd(), 'node_modules', parentThemePath))
|
21
|
+
? path.resolve(process.cwd(), 'node_modules', parentThemePath)
|
22
|
+
: false
|
23
|
+
// look for parent theme in relative path
|
24
|
+
const parentThemeRelative = fs.existsSync(path.resolve(process.cwd(), parentThemePath))
|
25
|
+
? path.resolve(process.cwd(), parentThemePath)
|
26
|
+
: false
|
27
|
+
// prefer node_modules over relative path
|
28
|
+
const parentTheme = parentThemePkg || parentThemeRelative
|
29
|
+
|
30
|
+
if (!parentTheme) {
|
31
|
+
removeParentThemeReference()
|
32
|
+
return
|
33
|
+
}
|
34
|
+
ThemeEnvy.parentTheme.path = parentTheme
|
35
|
+
// define elements and features arrays
|
36
|
+
ThemeEnvy.parentTheme.elements = listFeaturesOrElements({ type: 'elements' })
|
37
|
+
ThemeEnvy.parentTheme.features = listFeaturesOrElements({ type: 'features' })
|
38
|
+
})()
|
39
|
+
|
40
|
+
function removeParentThemeReference() {
|
41
|
+
delete ThemeEnvy.parentTheme
|
42
|
+
}
|
43
|
+
|
44
|
+
function listFeaturesOrElements({ type }) {
|
45
|
+
// get parentTheme config
|
46
|
+
const parentConfig = require(path.resolve(ThemeEnvy.parentTheme.path, 'parent.config.js'))
|
47
|
+
// get parentTheme theme-envy directory path
|
48
|
+
const parentThemeEnvyDir = path.resolve(ThemeEnvy.parentTheme.path, 'src/theme-envy')
|
49
|
+
|
50
|
+
const elementsOrFeatures = fs.readdirSync(path.resolve(parentThemeEnvyDir, type))
|
51
|
+
.filter(file => {
|
52
|
+
// remove items that are in the parent.config.js exclude array
|
53
|
+
if (!parentConfig[type]?.exclude) return true
|
54
|
+
return parentConfig[type].exclude.includes(path.parse(file).name) === false
|
55
|
+
})
|
56
|
+
.filter(file => {
|
57
|
+
// remove items that are in theme.config.js exclude array
|
58
|
+
if (!ThemeEnvy.parentTheme[type]?.exclude) return true
|
59
|
+
return ThemeEnvy.parentTheme[type]?.exclude.includes(path.parse(file).name) === false
|
60
|
+
})
|
61
|
+
.map(file => path.resolve(parentThemeEnvyDir, type, file).replace('.js', ''))
|
62
|
+
|
63
|
+
// add items that are in the include array of the child theme
|
64
|
+
if (ThemeEnvy.parentTheme[type]?.include) {
|
65
|
+
ThemeEnvy.parentTheme[type].include.forEach(item => {
|
66
|
+
if (!elementsOrFeatures.includes(item)) {
|
67
|
+
// check if the item exists in the parent theme
|
68
|
+
if (!fs.existsSync(path.resolve(parentThemeEnvyDir, type, item))) {
|
69
|
+
console.error(`The ${item} ${type} does not exist in the parent theme`)
|
70
|
+
return
|
71
|
+
}
|
72
|
+
elementsOrFeatures.push(path.resolve(ThemeEnvy.parentTheme.path, 'theme-envy', type, item))
|
73
|
+
}
|
74
|
+
})
|
75
|
+
}
|
76
|
+
return elementsOrFeatures
|
77
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
/**
|
2
|
+
* Get all files from the parent theme that are not in the child
|
3
|
+
* @private
|
4
|
+
* @param {function} func - function to get files with a glob, should return an array of file paths
|
5
|
+
* @param {array} childFiles - array of file paths from the child theme that we check against
|
6
|
+
* @returns {array} - array of file paths from the parent theme that are not in the child
|
7
|
+
*/
|
8
|
+
const path = require('path')
|
9
|
+
const { directories } = require('#EnsureDirectories')
|
10
|
+
// sets resolved parent theme path
|
11
|
+
require('./index.js')
|
12
|
+
|
13
|
+
module.exports = (func, childFiles, type) => {
|
14
|
+
const childRelative = childFiles.map(file => path.relative(ThemeEnvy.themePath, file))
|
15
|
+
// get all files from the parent theme, using only elements and features directories in parent theme
|
16
|
+
let only = [...ThemeEnvy.parentTheme.elements, ...ThemeEnvy.parentTheme.features]
|
17
|
+
|
18
|
+
if (type === 'elements') {
|
19
|
+
only = ThemeEnvy.parentTheme.elements
|
20
|
+
}
|
21
|
+
if (type === 'features') {
|
22
|
+
only = ThemeEnvy.parentTheme.features
|
23
|
+
}
|
24
|
+
if (type === 'schema') {
|
25
|
+
only.push(path.resolve(ThemeEnvy.parentTheme.path, 'src/theme-envy/schema'))
|
26
|
+
}
|
27
|
+
if (type === 'liquid') {
|
28
|
+
only.push(...directories.map(dir => path.resolve(ThemeEnvy.parentTheme.path, 'src', dir)))
|
29
|
+
}
|
30
|
+
if (type === 'sectionGroups') {
|
31
|
+
only.push(path.resolve(ThemeEnvy.parentTheme.path, 'src/sections'))
|
32
|
+
}
|
33
|
+
if (type === 'config') {
|
34
|
+
only.push(path.resolve(ThemeEnvy.parentTheme.path, 'src/config'))
|
35
|
+
}
|
36
|
+
if (type === 'locales') {
|
37
|
+
only.push(path.resolve(ThemeEnvy.parentTheme.path, 'src/locales'))
|
38
|
+
}
|
39
|
+
if (type === 'templates') {
|
40
|
+
only.push(path.resolve(ThemeEnvy.parentTheme.path, 'src/templates'))
|
41
|
+
}
|
42
|
+
if (type === 'criticalCSS') {
|
43
|
+
only.push(path.resolve(ThemeEnvy.parentTheme.path, 'src/styles'))
|
44
|
+
}
|
45
|
+
|
46
|
+
return func(ThemeEnvy.parentTheme.path, only).filter(file => !childRelative.includes(path.relative(ThemeEnvy.parentTheme.path, file)))
|
47
|
+
}
|
@@ -0,0 +1,88 @@
|
|
1
|
+
const prettier = require('prettier')
|
2
|
+
const { RuntimeGlobals } = require('webpack')
|
3
|
+
|
4
|
+
const pluginName = 'RetryChunkLoadPlugin'
|
5
|
+
|
6
|
+
class RetryChunkLoadPlugin {
|
7
|
+
constructor(options = {}) {
|
8
|
+
this.options = Object.assign({}, options)
|
9
|
+
}
|
10
|
+
|
11
|
+
apply(compiler) {
|
12
|
+
compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
|
13
|
+
const { mainTemplate, runtimeTemplate } = compilation
|
14
|
+
const maxRetryValueFromOptions = Number(this.options.maxRetries)
|
15
|
+
const maxRetries =
|
16
|
+
Number.isInteger(maxRetryValueFromOptions) &&
|
17
|
+
maxRetryValueFromOptions > 0
|
18
|
+
? maxRetryValueFromOptions
|
19
|
+
: 1
|
20
|
+
// const getCacheBustString = () =>
|
21
|
+
// this.options.cacheBust
|
22
|
+
// ? `
|
23
|
+
// (${this.options.cacheBust})();
|
24
|
+
// `
|
25
|
+
// : '"cache-bust=true"'
|
26
|
+
mainTemplate.hooks.localVars.tap(
|
27
|
+
{ name: pluginName, stage: 1 },
|
28
|
+
(source, chunk) => {
|
29
|
+
const currentChunkName = chunk.name
|
30
|
+
const addRetryCode =
|
31
|
+
!this.options.chunks ||
|
32
|
+
this.options.chunks.includes(currentChunkName)
|
33
|
+
if (!addRetryCode) return source
|
34
|
+
const script = runtimeTemplate.iife(
|
35
|
+
'',
|
36
|
+
`
|
37
|
+
if(typeof ${RuntimeGlobals.require} !== "undefined") {
|
38
|
+
var oldGetScript = ${RuntimeGlobals.getChunkScriptFilename};
|
39
|
+
var oldLoadScript = ${RuntimeGlobals.ensureChunk};
|
40
|
+
var queryMap = new Map();
|
41
|
+
var countMap = new Map();
|
42
|
+
${RuntimeGlobals.getChunkScriptFilename} = function(chunkId){
|
43
|
+
var result = oldGetScript(chunkId);
|
44
|
+
return result + (queryMap.has(chunkId) ? '?' + queryMap.get(chunkId) : '');
|
45
|
+
};
|
46
|
+
${RuntimeGlobals.ensureChunk} = function(chunkId){
|
47
|
+
var result = oldLoadScript(chunkId);
|
48
|
+
return result.catch(function(error){
|
49
|
+
var retries = countMap.has(chunkId) ? countMap.get(chunkId) : ${maxRetries};
|
50
|
+
if (retries < 1) {
|
51
|
+
var realSrc = oldGetScript(chunkId);
|
52
|
+
error.message = 'Loading chunk ' + chunkId + ' failed after ${maxRetries} retries.\\n(' + realSrc + ')';
|
53
|
+
error.request = realSrc;${
|
54
|
+
this.options.lastResortScript
|
55
|
+
? this.options.lastResortScript
|
56
|
+
: ''
|
57
|
+
}
|
58
|
+
throw error;
|
59
|
+
}
|
60
|
+
return new Promise(function (resolve) {
|
61
|
+
setTimeout(function () {
|
62
|
+
var retryAttempt = ${maxRetries} - retries + 1;
|
63
|
+
var retryAttemptString = '&retry-attempt=' + retryAttempt;
|
64
|
+
const cacheBust = Date.now();
|
65
|
+
queryMap.set(chunkId, cacheBust);
|
66
|
+
countMap.set(chunkId, retries - 1);
|
67
|
+
resolve(${RuntimeGlobals.ensureChunk}(chunkId));
|
68
|
+
}, ${this.options.retryDelay || this.options.timeout || 0})
|
69
|
+
})
|
70
|
+
});
|
71
|
+
};
|
72
|
+
}`
|
73
|
+
)
|
74
|
+
return (
|
75
|
+
source +
|
76
|
+
prettier.format(script, {
|
77
|
+
trailingComma: 'es5',
|
78
|
+
singleQuote: true,
|
79
|
+
parser: 'babel'
|
80
|
+
})
|
81
|
+
)
|
82
|
+
}
|
83
|
+
)
|
84
|
+
})
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
module.exports.RetryChunkLoadPlugin = RetryChunkLoadPlugin
|
@@ -8,6 +8,7 @@
|
|
8
8
|
* @returns {Promise}
|
9
9
|
*/
|
10
10
|
|
11
|
+
const path = require('path')
|
11
12
|
const webpack = require('webpack')
|
12
13
|
const getAll = require('./get-all')
|
13
14
|
|
@@ -25,12 +26,34 @@ module.exports = function({ mode, opts }) {
|
|
25
26
|
webpackConfig.watch = watch
|
26
27
|
// merge our theme config named entries into webackConfig.entry
|
27
28
|
webpackConfig.entry = { ...webpackConfig.entry, ...ThemeEnvy.entry }
|
29
|
+
// merge our theme config resolve aliases into webpackConfig.resolve.alias
|
30
|
+
if (ThemeEnvy.resolve?.alias) webpackConfig.resolve.alias = { ...webpackConfig.resolve.alias, ...ThemeEnvy.resolve.alias }
|
28
31
|
|
32
|
+
if (ThemeEnvy?.tailwind !== false) {
|
33
|
+
// if tailwind is enabled, we need to add our critical css to the entry list
|
34
|
+
webpackConfig.entry['theme-envy.critical'] = [`${ThemeEnvy.paths.build}/requires/styles/theme-envy.css`]
|
35
|
+
}
|
29
36
|
if (ThemeEnvy?.tailwind === false && getAll('criticalCSS').length > 0) {
|
30
37
|
// if tailwind is disabled, we need to add our critical css to the entry list
|
31
|
-
webpackConfig.entry['theme-envy.critical'] = `${ThemeEnvy.paths.build}/requires/styles/theme-envy.css`
|
38
|
+
webpackConfig.entry['theme-envy.critical'] = [`${ThemeEnvy.paths.build}/requires/styles/theme-envy.css`]
|
32
39
|
}
|
33
40
|
|
41
|
+
// check for Aliases and Entries in parent theme parent.config.js and merge them with our own
|
42
|
+
if (ThemeEnvy.parentTheme) {
|
43
|
+
const parentThemeConfig = require(path.resolve(ThemeEnvy.parentTheme.path, 'parent.config.js'))
|
44
|
+
// merge parent theme aliases into webpackConfig.resolve.alias
|
45
|
+
if (parentThemeConfig.resolve?.alias) webpackConfig.resolve.alias = { ...webpackConfig.resolve.alias, ...parentThemeConfig.resolve.alias }
|
46
|
+
// iterate over parent theme entries and add them to webpackConfig.entry, merging with our own into an array if necessary
|
47
|
+
if (parentThemeConfig.entry) {
|
48
|
+
for (const [key, value] of Object.entries(parentThemeConfig.entry)) {
|
49
|
+
if (webpackConfig.entry[key]) {
|
50
|
+
webpackConfig.entry[key] = [...webpackConfig.entry[key], ...value]
|
51
|
+
} else {
|
52
|
+
webpackConfig.entry[key] = [...value]
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
34
57
|
webpack(webpackConfig, (err, stats) => {
|
35
58
|
if (err || stats.hasErrors()) {
|
36
59
|
console.log(stats, err)
|
package/build/index.js
CHANGED
@@ -15,14 +15,15 @@
|
|
15
15
|
const path = require('path')
|
16
16
|
const chalk = require('chalk')
|
17
17
|
const emoji = require('node-emoji')
|
18
|
-
const { themeEnvy, webpack
|
18
|
+
const { themeEnvy, webpack } = require('#Build/functions')
|
19
19
|
const { distClean } = require('#Helpers')
|
20
20
|
|
21
21
|
module.exports = async function(env, opts = {}) {
|
22
22
|
const mode = env || 'production'
|
23
|
+
process.env.mode = mode
|
23
24
|
|
24
25
|
// empty dist folder for a clean build, do not log the clean message
|
25
|
-
distClean({ quiet: true })
|
26
|
+
if (opts.clean || !opts.watch) distClean({ quiet: true })
|
26
27
|
|
27
28
|
// our pretty build message
|
28
29
|
const relativeDistPath = path.relative(process.cwd(), ThemeEnvy.outputPath)
|
@@ -37,8 +38,6 @@ module.exports = async function(env, opts = {}) {
|
|
37
38
|
|
38
39
|
await themeEnvy({ mode, opts })
|
39
40
|
|
40
|
-
if (ThemeEnvy?.tailwind !== false) await tailwind({ mode, opts })
|
41
|
-
|
42
41
|
await webpack({ mode, opts })
|
43
42
|
|
44
43
|
ThemeEnvy.events.emit('build:complete')
|
package/build/requires/assets.js
CHANGED
@@ -4,9 +4,8 @@
|
|
4
4
|
|
5
5
|
const fs = require('fs-extra')
|
6
6
|
const path = require('path')
|
7
|
-
const
|
8
|
-
|
9
|
-
const assets = glob.sync(path.resolve(ThemeEnvy.themePath, '**/assets/**/*.*'))
|
7
|
+
const { getAll } = require('#Build/functions')
|
8
|
+
const assets = getAll('assets')
|
10
9
|
|
11
10
|
// if dist/assets doesn't exist create it
|
12
11
|
fs.ensureDirSync(path.resolve(ThemeEnvy.outputPath, 'assets'))
|
@@ -43,7 +43,6 @@ const path = require('path');
|
|
43
43
|
}
|
44
44
|
})
|
45
45
|
|
46
|
-
// TODO: LOOK INTO THIS
|
47
46
|
schema.forEach(entry => {
|
48
47
|
const file = `${entry.file}.liquid`
|
49
48
|
ThemeEnvy.schema[file] = ThemeEnvy.schema[file] || {
|
@@ -53,6 +52,6 @@ const path = require('path');
|
|
53
52
|
// add the schema to the destination, process entry.content for softlimit partials
|
54
53
|
// target is either 'settings' or 'blocks'
|
55
54
|
const target = entry.target.split('.')[1]
|
56
|
-
ThemeEnvy.schema[file][target].
|
55
|
+
ThemeEnvy.schema[file][target] = [...ThemeEnvy.schema[file][target], ...entry.content]
|
57
56
|
})
|
58
57
|
})()
|
@@ -16,10 +16,9 @@ const colors = require('ansi-colors');
|
|
16
16
|
failedHookInstalls: 1,
|
17
17
|
globals: 1,
|
18
18
|
liquid: getAll('liquid').length,
|
19
|
-
locales:
|
19
|
+
locales: getAll('locales').length,
|
20
20
|
requires: 7,
|
21
21
|
sectionGroups: 1,
|
22
|
-
tailwind: ThemeEnvy?.tailwind !== false ? 1 : 0,
|
23
22
|
templates: getAll('templates').length,
|
24
23
|
webpack: 1,
|
25
24
|
}
|
@@ -33,7 +33,9 @@ ThemeEnvy.events.on('watch:start', () => {
|
|
33
33
|
})
|
34
34
|
|
35
35
|
const ThemeRequire = (file, options) => {
|
36
|
-
//
|
36
|
+
// assume files are javascript if no extension given
|
37
|
+
if (!path.extname(file)) file = `${file}.js`
|
38
|
+
|
37
39
|
const ref = getFile(file)
|
38
40
|
|
39
41
|
if (ref.length === 0) {
|
@@ -87,10 +89,10 @@ const ThemeRequire = (file, options) => {
|
|
87
89
|
content = schemaExtend(content, options.extend)
|
88
90
|
}
|
89
91
|
if (options?.loop) {
|
90
|
-
content = schemaLoop(
|
92
|
+
content = schemaLoop(content, options.loop)
|
91
93
|
}
|
92
94
|
if (options?.suffix) {
|
93
|
-
content = schemaSuffix(
|
95
|
+
content = schemaSuffix(content, options.suffix)
|
94
96
|
}
|
95
97
|
|
96
98
|
if (options?.loader) {
|
@@ -101,7 +103,7 @@ const ThemeRequire = (file, options) => {
|
|
101
103
|
return content
|
102
104
|
}
|
103
105
|
|
104
|
-
function schemaDelete(
|
106
|
+
function schemaDelete(src, del) {
|
105
107
|
const replaceWith = src.filter(entry => {
|
106
108
|
if (entry.settings) {
|
107
109
|
entry.settings = schemaDelete(entry.settings, del)
|
@@ -130,7 +132,7 @@ function schemaExtend(src, exts) {
|
|
130
132
|
return replaceWith
|
131
133
|
}
|
132
134
|
|
133
|
-
function schemaLoop(
|
135
|
+
function schemaLoop(src, loop) {
|
134
136
|
const replaceWith = []
|
135
137
|
for (let i = 1; i < (loop + 1); i++) {
|
136
138
|
const srcCopy = parseJson(src)
|
@@ -145,7 +147,7 @@ function schemaLoop(file, src, loop) {
|
|
145
147
|
return replaceWith
|
146
148
|
}
|
147
149
|
|
148
|
-
function schemaSuffix(
|
150
|
+
function schemaSuffix(src, suffix) {
|
149
151
|
const replaceWith = parseJson(src)
|
150
152
|
replaceWith.map(entry => {
|
151
153
|
if (entry.id) entry.id = `${entry.id}_${suffix}`
|
@@ -165,8 +167,8 @@ function getFile(file) {
|
|
165
167
|
return getSchemaFile(file)
|
166
168
|
} else {
|
167
169
|
const res = glob.sync(path.resolve(ThemeEnvy.themePath, `**/${file}`))
|
168
|
-
if (ThemeEnvy.parentTheme && res.length === 0) {
|
169
|
-
res.push(...glob.sync(path.resolve(ThemeEnvy.parentTheme, `**/${file}`)))
|
170
|
+
if (ThemeEnvy.parentTheme.path && res.length === 0) {
|
171
|
+
res.push(...glob.sync(path.resolve(ThemeEnvy.parentTheme.path, `**/${file}`)))
|
170
172
|
}
|
171
173
|
return res
|
172
174
|
}
|
@@ -176,7 +178,7 @@ function getFile(file) {
|
|
176
178
|
@param src [object] - the file contents to be extended
|
177
179
|
@param schemaRef [string] - source schema file name being extended
|
178
180
|
*/
|
179
|
-
function parseJson(src
|
181
|
+
function parseJson(src) {
|
180
182
|
try {
|
181
183
|
return JSON.parse(JSON.stringify(src))
|
182
184
|
} catch (error) {
|
@@ -3,14 +3,19 @@
|
|
3
3
|
*/
|
4
4
|
const fs = require('fs-extra')
|
5
5
|
const path = require('path')
|
6
|
+
const { getAll } = require('#Build/functions')
|
6
7
|
|
7
|
-
|
8
|
+
const locales = getAll('locales')
|
9
|
+
|
10
|
+
// if dist/locales doesn't exist create it
|
8
11
|
fs.ensureDirSync(path.resolve(ThemeEnvy.outputPath, 'locales'))
|
9
12
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
locales.forEach(locale => {
|
14
|
+
try {
|
15
|
+
fs.copySync(locale, path.resolve(ThemeEnvy.outputPath, 'locales', path.basename(locale)))
|
16
|
+
// update progress bar
|
17
|
+
ThemeEnvy.progress.increment('locales', 1)
|
18
|
+
} catch (err) {
|
19
|
+
console.error(err)
|
20
|
+
}
|
21
|
+
})
|
@@ -7,38 +7,46 @@ const path = require('path')
|
|
7
7
|
const fs = require('fs-extra')
|
8
8
|
const { getAll } = require('#Build/functions')
|
9
9
|
const elements = getAll('elements').map(file => {
|
10
|
-
const name = path.basename === 'index.js' ? path.basename(path.dirname(file)) : path.basename(file, '.js')
|
10
|
+
const name = path.basename(file) === 'index.js' ? path.basename(path.dirname(file)) : path.basename(file, '.js')
|
11
11
|
return `'${name}': () => import(/* webpackChunkName: "${name}" */ '${file}')`
|
12
12
|
})
|
13
13
|
|
14
14
|
const markup = elements.length
|
15
|
-
? `const elements = {
|
15
|
+
? `const elements = {
|
16
|
+
${elements.join(',\n ')}}
|
16
17
|
// check all elements for presence in the Document and load if they are there
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
if (domElm
|
21
|
-
|
22
|
-
entries
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
}
|
18
|
+
function dynamicElements() {
|
19
|
+
Object.entries(elements).forEach(elm => {
|
20
|
+
const domElm = document.querySelector(elm[0])
|
21
|
+
if (domElm) {
|
22
|
+
if (domElm.getAttribute('loading') === 'lazy') {
|
23
|
+
const observer = new IntersectionObserver((entries, observer) => {
|
24
|
+
entries.forEach(entry => {
|
25
|
+
if (entry.isIntersecting) {
|
26
|
+
loadElement(elm)
|
27
|
+
domElm.setAttribute('loading', 'loaded')
|
28
|
+
observer.disconnect()
|
29
|
+
}
|
30
|
+
})
|
31
|
+
}, { rootMargin: '0px 0px 500px 0px', threshold: 0 })
|
32
|
+
observer.observe(domElm)
|
33
|
+
return
|
34
|
+
}
|
35
|
+
loadElement(elm)
|
36
|
+
}
|
37
|
+
})
|
38
|
+
}
|
39
|
+
dynamicElements()
|
36
40
|
function loadElement(elm) {
|
37
41
|
// load the element
|
38
42
|
elm[1]()
|
39
43
|
// remove from Object so we don't need to check again
|
40
44
|
delete elements[elm[0]]
|
41
45
|
}
|
46
|
+
// eslint-disable-next-line no-undef
|
47
|
+
if (Shopify.designMode) {
|
48
|
+
document.addEventListener('shopify:section:load', dynamicElements)
|
49
|
+
}
|
42
50
|
`
|
43
51
|
: ''
|
44
52
|
fs.writeFileSync(path.resolve(__dirname, '../elements.js'), markup, 'utf8')
|
@@ -4,7 +4,11 @@ const getAll = require('#Build/functions/get-all.js')
|
|
4
4
|
|
5
5
|
const criticalCSS = getAll('criticalCSS')
|
6
6
|
|
7
|
-
const markup = `${ThemeEnvy.tailwind !== false
|
7
|
+
const markup = `${ThemeEnvy.tailwind !== false
|
8
|
+
? `@import 'tailwindcss/base';
|
9
|
+
@import 'tailwindcss/components';
|
10
|
+
@import 'tailwindcss/utilities';`
|
11
|
+
: ''}
|
8
12
|
${criticalCSS.map(file => `@import '${file}';`).join('\n')}
|
9
13
|
`
|
10
14
|
|
@@ -1,12 +1,14 @@
|
|
1
|
+
const path = require('path')
|
2
|
+
|
3
|
+
const { EsbuildPlugin } = require('esbuild-loader')
|
4
|
+
const { RetryChunkLoadPlugin } = require('./functions/webpack-plugins/retry-chunk-load-plugin')
|
1
5
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
2
|
-
const TerserPlugin = require('terser-webpack-plugin')
|
3
6
|
const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts')
|
4
|
-
const
|
5
|
-
const path = require('path')
|
7
|
+
const TerserPlugin = require('terser-webpack-plugin')
|
6
8
|
|
7
9
|
module.exports = {
|
8
10
|
entry: {
|
9
|
-
'theme-envy': path.resolve(__dirname, 'requires/scripts/theme-envy.js'),
|
11
|
+
'theme-envy': [path.resolve(__dirname, 'requires/scripts/theme-envy.js')],
|
10
12
|
},
|
11
13
|
output: {
|
12
14
|
path: path.resolve(ThemeEnvy.outputPath, 'assets'),
|
@@ -25,6 +27,7 @@ module.exports = {
|
|
25
27
|
return '[name].js?h=[contenthash:5]'
|
26
28
|
}
|
27
29
|
},
|
30
|
+
devtool: false,
|
28
31
|
module: {
|
29
32
|
rules: [
|
30
33
|
{
|
@@ -44,7 +47,7 @@ module.exports = {
|
|
44
47
|
sideEffects: false,
|
45
48
|
minimizer: [
|
46
49
|
(process.env.mode === 'production')
|
47
|
-
? new
|
50
|
+
? new EsbuildPlugin({
|
48
51
|
target: 'es2015', // Syntax to compile to (see options below for possible values)
|
49
52
|
})
|
50
53
|
: new TerserPlugin({
|
@@ -56,16 +59,24 @@ module.exports = {
|
|
56
59
|
alias: {
|
57
60
|
Build: ThemeEnvy.paths.build,
|
58
61
|
Helpers: ThemeEnvy.paths.helpers,
|
59
|
-
Elements: path.resolve(ThemeEnvy.themePath, 'theme-envy/elements/'),
|
60
|
-
Features: path.resolve(ThemeEnvy.themePath, 'theme-envy/features/'),
|
61
62
|
Root: path.resolve(process.cwd()),
|
62
63
|
Scripts: path.resolve(ThemeEnvy.themePath, 'scripts/'),
|
63
64
|
},
|
65
|
+
modules: ['node_modules'],
|
66
|
+
},
|
67
|
+
resolveLoader: {
|
68
|
+
modules: ['node_modules'],
|
64
69
|
},
|
65
70
|
plugins: [
|
66
71
|
new RemoveEmptyScriptsPlugin(),
|
67
72
|
new MiniCssExtractPlugin({
|
68
73
|
filename: '[name].css?h=[chunkhash:5]',
|
69
74
|
}),
|
75
|
+
new RetryChunkLoadPlugin({
|
76
|
+
// optional value to set the amount of time in milliseconds before trying to load the chunk again. Default is 0
|
77
|
+
retryDelay: 0,
|
78
|
+
// optional value to set the maximum number of retries to load the chunk. Default is 1
|
79
|
+
maxRetries: 3
|
80
|
+
}),
|
70
81
|
]
|
71
82
|
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
const strings = {
|
2
|
+
schemaStart: '{% schema %}',
|
3
|
+
schemaEnd: '{% endschema %}',
|
4
|
+
}
|
5
|
+
|
6
|
+
module.exports = function(source, file) {
|
7
|
+
const schemaStart = source.indexOf(strings.schemaStart)
|
8
|
+
const schemaEnd = source.indexOf(strings.schemaEnd)
|
9
|
+
if (schemaStart === -1 || schemaEnd === -1) return source
|
10
|
+
try {
|
11
|
+
return {
|
12
|
+
schema: JSON.parse(source.slice(schemaStart + strings.schemaStart.length, schemaEnd)),
|
13
|
+
schemaStart,
|
14
|
+
schemaEnd
|
15
|
+
}
|
16
|
+
} catch (error) {
|
17
|
+
console.error(file, error)
|
18
|
+
process.exit()
|
19
|
+
}
|
20
|
+
}
|
@@ -12,7 +12,7 @@ const { spawn } = require('child_process')
|
|
12
12
|
|
13
13
|
module.exports = function() {
|
14
14
|
const relativeDistPath = path.relative(process.cwd(), ThemeEnvy.outputPath)
|
15
|
-
const themePull = ['theme', 'pull', `--store=${ThemeEnvy.store}`, `--path=${relativeDistPath}
|
15
|
+
const themePull = ['theme', 'pull', `--store=${ThemeEnvy.store}`, `--path=${relativeDistPath}`]
|
16
16
|
const shopify = spawn('shopify', themePull, { cwd: ThemeEnvy.outputPath, stdio: 'inherit' })
|
17
17
|
|
18
18
|
shopify.on('exit', function() {
|
package/helpers/index.js
CHANGED
@@ -5,6 +5,7 @@ module.exports = {
|
|
5
5
|
globalThemeEnvy: require('./functions/global-theme-envy.js'),
|
6
6
|
liquidPrettify: require('./functions/liquid-prettify.js'),
|
7
7
|
liquidTree: require('./functions/liquid-tree'),
|
8
|
+
parseSchema: require('./functions/parse-schema.js'),
|
8
9
|
pullJson: require('./functions/pull-json.js'),
|
9
10
|
scaffoldNew: require('./functions/scaffold-new'),
|
10
11
|
}
|
package/index.js
CHANGED
@@ -116,6 +116,7 @@ program
|
|
116
116
|
.description('Build Shopify theme')
|
117
117
|
.usage('[development|production] -w|--watch')
|
118
118
|
.addArgument(new commander.Argument('[env]', 'Specify the build environment to run').choices(['development', 'production']))
|
119
|
+
.option('-c, --clean', 'Clean output directory before building')
|
119
120
|
.option('-w, --watch', 'Watch for changed files and update dist, serve with Shopify CLI')
|
120
121
|
.option('-v, --verbose', 'Show Tailwind and Webpack in output')
|
121
122
|
.action((env, options, command) => {
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module.exports = {
|
2
|
-
plugins:
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
2
|
+
plugins: {
|
3
|
+
'postcss-import': {},
|
4
|
+
'tailwindcss/nesting': {},
|
5
|
+
tailwindcss: {},
|
6
|
+
autoprefixer: {},
|
7
|
+
cssnano: {}
|
8
|
+
}
|
9
9
|
}
|
@@ -2,15 +2,29 @@
|
|
2
2
|
* @private
|
3
3
|
* @type {import('tailwindcss').Config}
|
4
4
|
*/
|
5
|
-
const theme = require('./theme.config.js')
|
6
5
|
const path = require('path')
|
7
6
|
const ThemeEnvy = require('./theme.config.js')
|
8
7
|
|
9
|
-
|
10
|
-
content: [
|
8
|
+
const config = {
|
9
|
+
content: [
|
10
|
+
path.resolve(ThemeEnvy.themePath, '**/*.{liquid,js}'),
|
11
|
+
],
|
11
12
|
theme: {
|
12
13
|
extend: {},
|
13
14
|
},
|
14
|
-
screens:
|
15
|
+
screens: ThemeEnvy.breakpoints,
|
15
16
|
plugins: [],
|
16
17
|
}
|
18
|
+
|
19
|
+
// merge parentTheme tailwind config if it exists
|
20
|
+
if (ThemeEnvy.parentTheme?.path) {
|
21
|
+
const parentTailwind = require(path.join(path.dirname(require.resolve(ThemeEnvy.parentTheme.path)), 'tailwind.config.js'))
|
22
|
+
// deep merge parentTheme tailwind config with child theme tailwind config
|
23
|
+
if (parentTailwind) {
|
24
|
+
config.theme.extend = Object.assign(config.theme.extend, parentTailwind.theme.extend)
|
25
|
+
config.plugins = config.plugins.concat(parentTailwind.plugins)
|
26
|
+
config.content = config.content.concat(parentTailwind.content)
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
module.exports = config
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@softlimit/theme-envy",
|
3
|
-
"version": "0.1.
|
3
|
+
"version": "0.1.4-alpha",
|
4
4
|
"description": "Softlimit Shopify Theme Development Environment",
|
5
5
|
"bin": {
|
6
6
|
"theme-envy": "./index.js"
|
@@ -28,7 +28,6 @@
|
|
28
28
|
"#Init/functions": "./init/functions/index.js",
|
29
29
|
"#Init/functions/*": "./init/functions/*",
|
30
30
|
"#LogSymbols": "./helpers/functions/log-symbols.js",
|
31
|
-
"#ParentTheme": "./parent-theme/index.js",
|
32
31
|
"#Root/*": "./*"
|
33
32
|
},
|
34
33
|
"repository": {
|
@@ -52,7 +51,7 @@
|
|
52
51
|
"dependencies": {
|
53
52
|
"@shopify/prettier-plugin-liquid": "^1.0.6",
|
54
53
|
"ansi-colors": "^4.1.3",
|
55
|
-
"autoprefixer": "^10.4.
|
54
|
+
"autoprefixer": "^10.4.14",
|
56
55
|
"chalk": "^4.1.2",
|
57
56
|
"chokidar": "^3.5.3",
|
58
57
|
"cli-progress": "^3.12.0",
|
@@ -61,7 +60,7 @@
|
|
61
60
|
"cssnano": "^5.1.15",
|
62
61
|
"esbuild-loader": "^3.0.1",
|
63
62
|
"fs-extra": "^11.1.0",
|
64
|
-
"glob": "^
|
63
|
+
"glob": "^10.2.1",
|
65
64
|
"mini-css-extract-plugin": "^2.7.2",
|
66
65
|
"node-emoji": "^1.11.0",
|
67
66
|
"path": "^0.12.7",
|
@@ -71,13 +70,14 @@
|
|
71
70
|
"promptly": "^3.2.0",
|
72
71
|
"simple-git": "^3.17.0",
|
73
72
|
"stmux": "^1.8.5",
|
74
|
-
"tailwindcss": "^3.
|
73
|
+
"tailwindcss": "^3.3.1",
|
75
74
|
"terser-webpack-plugin": "^5.3.6",
|
76
75
|
"webpack": "^5.77.0",
|
77
76
|
"webpack-cli": "^5.0.1",
|
78
77
|
"webpack-remove-empty-scripts": "^1.0.1"
|
79
78
|
},
|
80
79
|
"devDependencies": {
|
80
|
+
"caller": "^1.1.0",
|
81
81
|
"eslint": "^8.34.0",
|
82
82
|
"eslint-config-standard": "^17.0.0",
|
83
83
|
"eslint-plugin-import": "^2.27.5",
|
@@ -1,25 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Get all files from the parent theme that are not in the child
|
3
|
-
* @private
|
4
|
-
* @param {function} func - function to get files with a glob, should return an array of file paths
|
5
|
-
* @param {array} childFiles - array of file paths from the child theme that we check against
|
6
|
-
* @returns {array} - array of file paths from the parent theme that are not in the child
|
7
|
-
*/
|
8
|
-
const path = require('path')
|
9
|
-
const { directories } = require('#EnsureDirectories')
|
10
|
-
|
11
|
-
module.exports = (func, childFiles, type) => {
|
12
|
-
const childRelative = childFiles.map(file => path.relative(ThemeEnvy.themePath, file))
|
13
|
-
// get all files from the parent theme, using only directories listed in ThemeConfig
|
14
|
-
const only = [...ThemeEnvy.parentTheme.elements, ...ThemeEnvy.parentTheme.features]
|
15
|
-
if (type === 'schema') {
|
16
|
-
only.push('schema')
|
17
|
-
}
|
18
|
-
if (type === 'liquid') {
|
19
|
-
only.push(...directories.map(dir => path.resolve(ThemeEnvy.parentTheme, dir)))
|
20
|
-
}
|
21
|
-
if (type === 'sectionGroups') {
|
22
|
-
only.push(path.resolve(ThemeEnvy.parentTheme, 'sections'))
|
23
|
-
}
|
24
|
-
return func(ThemeEnvy.parentTheme, only).filter(file => !childRelative.includes(path.relative(ThemeEnvy.parentTheme, file)))
|
25
|
-
}
|
@@ -1,31 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* @private
|
3
|
-
* @file Runs Tailwind using the Tailwind CLI during build
|
4
|
-
* @see https://tailwindcss.com/docs/installation#using-tailwind-cli
|
5
|
-
*/
|
6
|
-
const { spawn } = require('child_process')
|
7
|
-
const path = require('path')
|
8
|
-
|
9
|
-
module.exports = function({ mode, opts }) {
|
10
|
-
return new Promise((resolve, reject) => {
|
11
|
-
const watch = opts.watch || false
|
12
|
-
const verbose = opts.verbose || false
|
13
|
-
|
14
|
-
// run tailwind
|
15
|
-
const tailwindCss = path.resolve(__dirname, '../requires/styles/theme-envy.css')
|
16
|
-
const tailwindOpts = ['tailwindcss', 'build', '-i', tailwindCss, '-o', `${ThemeEnvy.outputPath}/assets/theme-envy.critical.css`]
|
17
|
-
if (mode === 'production') tailwindOpts.push('--minify')
|
18
|
-
if (watch) tailwindOpts.push('--watch')
|
19
|
-
const tailwindOutput = verbose ? { stdio: 'inherit' } : {}
|
20
|
-
const tailwindProcess = spawn('npx', tailwindOpts, tailwindOutput)
|
21
|
-
if (watch) {
|
22
|
-
// watch process does not exit, so we need to increment the progress bar and resolve the promise
|
23
|
-
ThemeEnvy.progress.increment('tailwind')
|
24
|
-
resolve()
|
25
|
-
}
|
26
|
-
tailwindProcess.on('exit', () => {
|
27
|
-
ThemeEnvy.progress.increment('tailwind')
|
28
|
-
resolve()
|
29
|
-
})
|
30
|
-
})
|
31
|
-
}
|
@@ -1,28 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* @private
|
3
|
-
* @file Checks for a parent theme in node_modules or relative path and sets ThemeEnvy.parentTheme to the path
|
4
|
-
*/
|
5
|
-
|
6
|
-
const fs = require('fs-extra')
|
7
|
-
const path = require('path');
|
8
|
-
|
9
|
-
(() => {
|
10
|
-
const ThemeConfigPath = path.resolve(process.cwd(), 'theme.config.js')
|
11
|
-
if (!fs.existsSync(ThemeConfigPath)) return
|
12
|
-
ThemeEnvy.childPreferred = ThemeEnvy.childPreferred || []
|
13
|
-
|
14
|
-
// look for parent theme in node_modules
|
15
|
-
const parentThemePath = ThemeEnvy.parentTheme?.path
|
16
|
-
if (!parentThemePath) return
|
17
|
-
const parentThemePkg = fs.existsSync(path.resolve(process.cwd(), 'node_modules', parentThemePath))
|
18
|
-
? path.resolve(process.cwd(), 'node_modules', parentThemePath)
|
19
|
-
: false
|
20
|
-
// look for parent theme in relative path
|
21
|
-
const parentThemeRelative = fs.existsSync(path.resolve(process.cwd(), parentThemePath))
|
22
|
-
? path.resolve(process.cwd(), parentThemePath)
|
23
|
-
: false
|
24
|
-
// prefer node_modules over relative path
|
25
|
-
const parentTheme = parentThemePkg || parentThemeRelative
|
26
|
-
|
27
|
-
ThemeEnvy.parentTheme = parentTheme
|
28
|
-
})()
|