@softlimit/theme-envy 0.1.2-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +26 -0
- package/.github/CODEOWNERS +1 -0
- package/.github/workflows/release-please.yml +19 -0
- package/LICENSE +21 -0
- package/README.md +259 -0
- package/build/functions/failed-hook-installs.js +18 -0
- package/build/functions/get-all.js +102 -0
- package/build/functions/index.js +7 -0
- package/build/functions/liquid/functions/extend-liquid.js +77 -0
- package/build/functions/liquid/functions/flatten-shopify-directory-structure.js +29 -0
- package/build/functions/liquid/functions/index.js +6 -0
- package/build/functions/liquid/functions/list-dependencies.js +47 -0
- package/build/functions/liquid/functions/section-schema-inject.js +26 -0
- package/build/functions/liquid/index.js +36 -0
- package/build/functions/parent-theme-files.js +25 -0
- package/build/functions/tailwind.js +31 -0
- package/build/functions/theme-envy.js +85 -0
- package/build/functions/webpack.js +44 -0
- package/build/index.js +45 -0
- package/build/requires/assets.js +22 -0
- package/build/requires/config.js +44 -0
- package/build/requires/globals/index.js +10 -0
- package/build/requires/globals/parent-theme.js +28 -0
- package/build/requires/globals/prepare-install-hooks-schema.js +58 -0
- package/build/requires/globals/progress-bar.js +52 -0
- package/build/requires/globals/theme-require.js +190 -0
- package/build/requires/index.js +21 -0
- package/build/requires/locales.js +16 -0
- package/build/requires/scripts/index.js +5 -0
- package/build/requires/scripts/public-path.js +9 -0
- package/build/requires/scripts/script-builders/elements.build.js +44 -0
- package/build/requires/scripts/script-builders/features.build.js +15 -0
- package/build/requires/scripts/theme-envy.js +7 -0
- package/build/requires/snippets/index.js +11 -0
- package/build/requires/snippets/liquid-builders/theme-envy.liquid.build.js +21 -0
- package/build/requires/styles/index.js +1 -0
- package/build/requires/styles/styles-builders/theme-envy.css.js +11 -0
- package/build/requires/styles/tailwind-base.css +3 -0
- package/build/requires/templates.js +20 -0
- package/build/theme-envy.config.js +71 -0
- package/convert/functions/convert-sections-to-features.js +56 -0
- package/convert/functions/detect-children.js +30 -0
- package/convert/functions/index.js +6 -0
- package/convert/functions/install-hooks.js +68 -0
- package/convert/functions/set-settings-schema-js.js +14 -0
- package/convert/index.js +50 -0
- package/helpers/functions/dev.js +15 -0
- package/helpers/functions/dist-clean.js +19 -0
- package/helpers/functions/ensure-directories.js +26 -0
- package/helpers/functions/find-orphans.js +20 -0
- package/helpers/functions/global-theme-envy.js +20 -0
- package/helpers/functions/liquid-prettify.js +24 -0
- package/helpers/functions/liquid-tree/functions/count-results.js +13 -0
- package/helpers/functions/liquid-tree/functions/get-depth.js +12 -0
- package/helpers/functions/liquid-tree/functions/get-file-info.js +11 -0
- package/helpers/functions/liquid-tree/functions/index.js +5 -0
- package/helpers/functions/liquid-tree/index.js +48 -0
- package/helpers/functions/liquid-tree/objects/dependencies.js +74 -0
- package/helpers/functions/liquid-tree/objects/index.js +3 -0
- package/helpers/functions/log-symbols.js +28 -0
- package/helpers/functions/pull-json.js +22 -0
- package/helpers/functions/scaffold-new/functions/element.js +15 -0
- package/helpers/functions/scaffold-new/functions/feature.js +76 -0
- package/helpers/functions/scaffold-new/functions/index.js +6 -0
- package/helpers/functions/scaffold-new/functions/load-dir.js +24 -0
- package/helpers/functions/scaffold-new/functions/starter-content.js +21 -0
- package/helpers/functions/scaffold-new/functions/upper-first-letter.js +3 -0
- package/helpers/functions/scaffold-new/index.js +28 -0
- package/helpers/functions/scaffold-new/objects/index.js +3 -0
- package/helpers/functions/scaffold-new/objects/starter-configs.js +7 -0
- package/helpers/functions/unicode-supported.js +21 -0
- package/helpers/index.js +10 -0
- package/index.js +190 -0
- package/init/functions/add-theme-envy-features/features/theme-envy/install.js +6 -0
- package/init/functions/add-theme-envy-features/index.js +21 -0
- package/init/functions/copy-example-feature/example-feature/config/_example-feature.js +8 -0
- package/init/functions/copy-example-feature/example-feature/index.js +5 -0
- package/init/functions/copy-example-feature/example-feature/install.js +10 -0
- package/init/functions/copy-example-feature/example-feature/partials/_example-feature-partial.liquid +3 -0
- package/init/functions/copy-example-feature/example-feature/schema/schema-example-feature-schema-partial.js +16 -0
- package/init/functions/copy-example-feature/example-feature/schema/schema-example-feature-section.js +14 -0
- package/init/functions/copy-example-feature/example-feature/scripts/example-feature.js +3 -0
- package/init/functions/copy-example-feature/example-feature/sections/example-feature-section.liquid +11 -0
- package/init/functions/copy-example-feature/example-feature/snippets/example-feature-snippet.liquid +1 -0
- package/init/functions/copy-example-feature/index.js +25 -0
- package/init/functions/copy-starter-config-files/configs/postcss.config.js +9 -0
- package/init/functions/copy-starter-config-files/configs/tailwind.config.js +16 -0
- package/init/functions/copy-starter-config-files/configs/theme.config.js +25 -0
- package/init/functions/copy-starter-config-files/index.js +28 -0
- package/init/functions/copy-starter-config-files/utils/starter-config.js +17 -0
- package/init/functions/copy-starter-config-files/utils/starter-element.js +16 -0
- package/init/functions/copy-starter-config-files/utils/starter-install.js +14 -0
- package/init/functions/copy-starter-config-files/utils/starter-schema.js +32 -0
- package/init/functions/copy-starter-config-files/utils/starter-section.js +16 -0
- package/init/functions/create-empty-settings-data.js +19 -0
- package/init/functions/create-settings-schema.js +29 -0
- package/init/functions/if-shopify-theme-exists.js +26 -0
- package/init/functions/import-from-git.js +21 -0
- package/init/functions/index.js +10 -0
- package/init/functions/validate-source-theme.js +28 -0
- package/init/index.js +87 -0
- package/package.json +88 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Require all files in the requires directory
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
require('./globals')
|
|
6
|
+
|
|
7
|
+
const requires = [
|
|
8
|
+
'./assets',
|
|
9
|
+
'./config',
|
|
10
|
+
'./locales',
|
|
11
|
+
'./scripts',
|
|
12
|
+
'./snippets',
|
|
13
|
+
'./styles',
|
|
14
|
+
'./templates',
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
// iterate over requires so we can fire progress one at a time
|
|
18
|
+
requires.forEach((requirePath) => {
|
|
19
|
+
require(requirePath)
|
|
20
|
+
ThemeEnvy.progress.increment('requires', 1)
|
|
21
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file Copies all locales files from the src folder to the dist folder with no transformations
|
|
3
|
+
*/
|
|
4
|
+
const fs = require('fs-extra')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
|
|
7
|
+
// if dist/locales does not exist, create it
|
|
8
|
+
fs.ensureDirSync(path.resolve(ThemeEnvy.outputPath, 'locales'))
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
fs.copySync(path.resolve(ThemeEnvy.themePath, 'locales'), path.resolve(ThemeEnvy.outputPath, 'locales'))
|
|
12
|
+
// update progress bar
|
|
13
|
+
ThemeEnvy.progress.increment('locales')
|
|
14
|
+
} catch (err) {
|
|
15
|
+
console.error(err)
|
|
16
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Sets the public path for Webpack to use in dynamic imports
|
|
3
|
+
* @see https://webpack.js.org/guides/public-path/#on-the-fly
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* eslint-disable no-undef */
|
|
7
|
+
/* eslint-disable camelcase */
|
|
8
|
+
// window.ThemeEnvy.publicPath is set in the theme-envy.liquid snippet
|
|
9
|
+
__webpack_public_path__ = window.ThemeEnvy.publicPath
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Builds our elements.js file based on the elements in our src/elements directory
|
|
3
|
+
* @description Lazy loads our elements based on their presence in the DOM and the loading attribute
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path')
|
|
7
|
+
const fs = require('fs-extra')
|
|
8
|
+
const { getAll } = require('#Build/functions')
|
|
9
|
+
const elements = getAll('elements').map(file => {
|
|
10
|
+
const name = path.basename === 'index.js' ? path.basename(path.dirname(file)) : path.basename(file, '.js')
|
|
11
|
+
return `'${name}': () => import(/* webpackChunkName: "${name}" */ '${file}')`
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const markup = elements.length
|
|
15
|
+
? `const elements = {${elements.join(',\n')}}
|
|
16
|
+
// check all elements for presence in the Document and load if they are there
|
|
17
|
+
Object.entries(elements).forEach(elm => {
|
|
18
|
+
const domElm = document.querySelector(elm[0])
|
|
19
|
+
if (domElm) {
|
|
20
|
+
if (domElm.getAttribute('loading') === 'lazy') {
|
|
21
|
+
const observer = new IntersectionObserver((entries, observer) => {
|
|
22
|
+
entries.forEach(entry => {
|
|
23
|
+
if (entry.isIntersecting) {
|
|
24
|
+
loadElement(elm)
|
|
25
|
+
domElm.setAttribute('loading', 'loaded')
|
|
26
|
+
observer.disconnect()
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
}, { rootMargin: '0px 0px 500px 0px', threshold: 0 })
|
|
30
|
+
observer.observe(domElm)
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
loadElement(elm)
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
function loadElement(elm) {
|
|
37
|
+
// load the element
|
|
38
|
+
elm[1]()
|
|
39
|
+
// remove from Object so we don't need to check again
|
|
40
|
+
delete elements[elm[0]]
|
|
41
|
+
}
|
|
42
|
+
`
|
|
43
|
+
: ''
|
|
44
|
+
fs.writeFileSync(path.resolve(__dirname, '../elements.js'), markup, 'utf8')
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Builds our features.js file imports based on the features directory
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const fs = require('fs-extra')
|
|
7
|
+
const { getAll } = require('#Build/functions')
|
|
8
|
+
|
|
9
|
+
const features = getAll('features').map(file => {
|
|
10
|
+
return `import '${file}'`
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
const markup = features.join('\n')
|
|
14
|
+
|
|
15
|
+
fs.writeFileSync(path.resolve(__dirname, '../features.js'), markup, 'utf8')
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file Pre-builds the theme-envy.liquid snippet and adds it to dist/snippets
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs-extra')
|
|
6
|
+
const path = require('path')
|
|
7
|
+
require('./liquid-builders/theme-envy.liquid.build')
|
|
8
|
+
|
|
9
|
+
fs.ensureDirSync(path.resolve(ThemeEnvy.outputPath, 'snippets'))
|
|
10
|
+
|
|
11
|
+
fs.copyFileSync(path.resolve(__dirname, 'theme-envy.liquid'), path.resolve(ThemeEnvy.outputPath, 'snippets/theme-envy.liquid'))
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Builds the theme-envy.liquid snippet, which is used to load the theme-envy.js script and critical css
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const fs = require('fs-extra')
|
|
7
|
+
const getAll = require('#Build/functions/get-all.js')
|
|
8
|
+
const hasCriticalCSS = getAll('criticalCSS').length > 0 || ThemeEnvy?.tailwind !== false
|
|
9
|
+
|
|
10
|
+
const markup = `{% comment %} This file is auto-generated by theme-envy. DO NOT EDIT. {% endcomment %}
|
|
11
|
+
${hasCriticalCSS ? '{{ \'theme-envy.critical.css\' | asset_url | stylesheet_tag: preload: true }}' : ''}
|
|
12
|
+
<script>
|
|
13
|
+
(function() {
|
|
14
|
+
const assetUrl = {{ 'image.jpg' | asset_url | json }};
|
|
15
|
+
window.ThemeEnvy = window.ThemeEnvy || {};
|
|
16
|
+
window.ThemeEnvy.publicPath = assetUrl.substring(0, assetUrl.lastIndexOf('/') + 1);
|
|
17
|
+
})();
|
|
18
|
+
</script>
|
|
19
|
+
<script src="{{ 'theme-envy.js' | asset_url }}" defer></script>`
|
|
20
|
+
|
|
21
|
+
fs.writeFileSync(path.resolve(__dirname, '../theme-envy.liquid'), markup, 'utf8')
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require('./styles-builders/theme-envy.css.js')
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const fs = require('fs-extra')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const getAll = require('#Build/functions/get-all.js')
|
|
4
|
+
|
|
5
|
+
const criticalCSS = getAll('criticalCSS')
|
|
6
|
+
|
|
7
|
+
const markup = `${ThemeEnvy.tailwind !== false ? "@import './tailwind-base.css'" : ''};
|
|
8
|
+
${criticalCSS.map(file => `@import '${file}';`).join('\n')}
|
|
9
|
+
`
|
|
10
|
+
|
|
11
|
+
fs.writeFileSync(path.resolve(__dirname, '../theme-envy.css'), markup, 'utf8')
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Copies all templates from the src folder to the dist folder with no transformations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const fs = require('fs-extra')
|
|
7
|
+
const { getAll } = require('#Build/functions')
|
|
8
|
+
// copy all templates to dist
|
|
9
|
+
const globbedTemplates = getAll('templates')
|
|
10
|
+
|
|
11
|
+
const templateOutputPath = path.resolve(ThemeEnvy.outputPath, 'templates')
|
|
12
|
+
|
|
13
|
+
// if dist/templates doesn't exist, create it
|
|
14
|
+
fs.ensureDirSync(templateOutputPath)
|
|
15
|
+
globbedTemplates.forEach(file => {
|
|
16
|
+
// write each file to dist
|
|
17
|
+
fs.copyFileSync(file, path.resolve(templateOutputPath, path.basename(file)))
|
|
18
|
+
// update progress bar
|
|
19
|
+
ThemeEnvy.progress.increment('templates', 1)
|
|
20
|
+
})
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
|
2
|
+
const TerserPlugin = require('terser-webpack-plugin')
|
|
3
|
+
const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts')
|
|
4
|
+
const { ESBuildMinifyPlugin } = require('esbuild-loader')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
entry: {
|
|
9
|
+
'theme-envy': path.resolve(__dirname, 'requires/scripts/theme-envy.js'),
|
|
10
|
+
},
|
|
11
|
+
output: {
|
|
12
|
+
path: path.resolve(ThemeEnvy.outputPath, 'assets'),
|
|
13
|
+
publicPath: '',
|
|
14
|
+
filename: '[name].js?h=[contenthash:5]',
|
|
15
|
+
chunkFilename: (pathData) => {
|
|
16
|
+
// trim filenames so they're not super long... append 5 character hash
|
|
17
|
+
const name = pathData.chunk.name
|
|
18
|
+
const id = String(name || pathData.chunk.id).replace('node_modules_', '').substring(0, 25)
|
|
19
|
+
if (name === undefined) {
|
|
20
|
+
return `shared-${id}${pathData.chunk.contentHash.javascript.substring(0, 5)}.js?h=[contenthash:5]`
|
|
21
|
+
}
|
|
22
|
+
if (id.length > 25) {
|
|
23
|
+
return `${id}${pathData.chunk.contentHash.javascript.substring(0, 5)}.js?h=[contenthash:5]`
|
|
24
|
+
}
|
|
25
|
+
return '[name].js?h=[contenthash:5]'
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
module: {
|
|
29
|
+
rules: [
|
|
30
|
+
{
|
|
31
|
+
test: /(\.css)$/,
|
|
32
|
+
use: [
|
|
33
|
+
MiniCssExtractPlugin.loader,
|
|
34
|
+
{ loader: 'css-loader', options: { url: false } },
|
|
35
|
+
'postcss-loader',
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
optimization: {
|
|
41
|
+
splitChunks: {
|
|
42
|
+
automaticNameDelimiter: '_'
|
|
43
|
+
},
|
|
44
|
+
sideEffects: false,
|
|
45
|
+
minimizer: [
|
|
46
|
+
(process.env.mode === 'production')
|
|
47
|
+
? new ESBuildMinifyPlugin({
|
|
48
|
+
target: 'es2015', // Syntax to compile to (see options below for possible values)
|
|
49
|
+
})
|
|
50
|
+
: new TerserPlugin({
|
|
51
|
+
extractComments: false,
|
|
52
|
+
}),
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
resolve: {
|
|
56
|
+
alias: {
|
|
57
|
+
Build: ThemeEnvy.paths.build,
|
|
58
|
+
Helpers: ThemeEnvy.paths.helpers,
|
|
59
|
+
Elements: path.resolve(ThemeEnvy.themePath, 'theme-envy/elements/'),
|
|
60
|
+
Features: path.resolve(ThemeEnvy.themePath, 'theme-envy/features/'),
|
|
61
|
+
Root: path.resolve(process.cwd()),
|
|
62
|
+
Scripts: path.resolve(ThemeEnvy.themePath, 'scripts/'),
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
plugins: [
|
|
66
|
+
new RemoveEmptyScriptsPlugin(),
|
|
67
|
+
new MiniCssExtractPlugin({
|
|
68
|
+
filename: '[name].css?h=[chunkhash:5]',
|
|
69
|
+
}),
|
|
70
|
+
]
|
|
71
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const fs = require('fs-extra')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const glob = require('glob')
|
|
4
|
+
const logSymbols = require('#LogSymbols')
|
|
5
|
+
const detectChildren = require('./detect-children')
|
|
6
|
+
|
|
7
|
+
module.exports = function({ sourceTheme }) {
|
|
8
|
+
// figure out if we can separate any sections into features
|
|
9
|
+
const sections = glob.sync(path.resolve(sourceTheme, 'sections/*.liquid'))
|
|
10
|
+
|
|
11
|
+
const children = sections.map(section => {
|
|
12
|
+
// regexp matching for render and include tags
|
|
13
|
+
let results = detectChildren({ section, filePath: section, root: sourceTheme })
|
|
14
|
+
if (results.snippets.length) {
|
|
15
|
+
results.snippets.forEach(snippet => {
|
|
16
|
+
const snippetPath = path.resolve(sourceTheme, `snippets/${snippet}.liquid`)
|
|
17
|
+
const snippetChildren = detectChildren({ section, filePath: snippetPath })
|
|
18
|
+
const snippets = [...results.snippets, ...snippetChildren.snippets]
|
|
19
|
+
const assets = [...results.assets, ...snippetChildren.assets]
|
|
20
|
+
results = { section, snippets: [...new Set(snippets)], assets: [...new Set(assets)] }
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
return results
|
|
24
|
+
})
|
|
25
|
+
// determine if any sections have any snippets or any assets that are unique to that section
|
|
26
|
+
const sectionsWithUniqueChildren = children.map(child => {
|
|
27
|
+
const snippets = child.snippets.filter(snippet => {
|
|
28
|
+
return !children.find(section => section.section !== child.section && section.snippets.includes(snippet))
|
|
29
|
+
})
|
|
30
|
+
const assets = child.assets.filter(asset => {
|
|
31
|
+
return !children.find(section => section.section !== child.section && section.assets.includes(asset))
|
|
32
|
+
})
|
|
33
|
+
return { section: child.section, snippets, assets }
|
|
34
|
+
}).filter(section => section.snippets.length || section.assets.length)
|
|
35
|
+
|
|
36
|
+
// move sections with unique children to theme-envy/features
|
|
37
|
+
sectionsWithUniqueChildren.forEach(section => {
|
|
38
|
+
const sectionName = path.basename(section.section, '.liquid')
|
|
39
|
+
fs.ensureDirSync(path.resolve(sourceTheme, 'theme-envy/features', sectionName))
|
|
40
|
+
fs.moveSync(section.section, path.resolve(sourceTheme, 'theme-envy/features', sectionName, `sections/${sectionName}.liquid`))
|
|
41
|
+
if (section.snippets.length) {
|
|
42
|
+
fs.ensureDirSync(path.resolve(sourceTheme, 'theme-envy/features', sectionName, 'snippets'))
|
|
43
|
+
section.snippets.forEach(snippet => {
|
|
44
|
+
fs.moveSync(path.resolve(sourceTheme, `snippets/${snippet}.liquid`), path.resolve(sourceTheme, 'theme-envy/features', sectionName, 'snippets', `${snippet}.liquid`))
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
if (section.assets.length) {
|
|
48
|
+
fs.ensureDirSync(path.resolve(sourceTheme, 'theme-envy/features', sectionName, 'assets'))
|
|
49
|
+
section.assets.forEach(asset => {
|
|
50
|
+
fs.moveSync(path.resolve(sourceTheme, `assets/${asset}`), path.resolve(sourceTheme, 'theme-envy/features', sectionName, 'assets', `${asset}`))
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
console.log(`${logSymbols.success} Theme sections converted to features`)
|
|
56
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const fs = require('fs-extra')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
module.exports = function({ section, filePath, root }) {
|
|
5
|
+
try {
|
|
6
|
+
const source = fs.readFileSync(filePath, 'utf8')
|
|
7
|
+
const snippetTags = /\s*((include|render)\s['|"](\S*)['|"])\s*/gm
|
|
8
|
+
const snippets = [...source.matchAll(snippetTags)].map(tag => tag[3]).filter(snippet => {
|
|
9
|
+
// file exists
|
|
10
|
+
return fs.existsSync(path.resolve(process.cwd(), root, `snippets/${snippet}.liquid`))
|
|
11
|
+
})
|
|
12
|
+
const assetUrl = /['|"](\S*)['|"]\s*[|]\s*asset_url/gm
|
|
13
|
+
const assets = [...source.matchAll(assetUrl)].map(tag => tag[1]).filter(asset => {
|
|
14
|
+
// file exists
|
|
15
|
+
return fs.existsSync(path.resolve(process.cwd(), root, `assets/${asset}`))
|
|
16
|
+
})
|
|
17
|
+
// return unique values
|
|
18
|
+
return {
|
|
19
|
+
section,
|
|
20
|
+
snippets: [...new Set(snippets)],
|
|
21
|
+
assets: [...new Set(assets)]
|
|
22
|
+
}
|
|
23
|
+
} catch (e) {
|
|
24
|
+
return {
|
|
25
|
+
section,
|
|
26
|
+
snippets: [],
|
|
27
|
+
assets: []
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const fs = require('fs-extra')
|
|
3
|
+
const logSymbols = require('#LogSymbols')
|
|
4
|
+
const { liquidPrettify } = require('#Helpers')
|
|
5
|
+
|
|
6
|
+
const hooks = {
|
|
7
|
+
'<head>': {
|
|
8
|
+
after: "{% hook 'head-start' %}",
|
|
9
|
+
},
|
|
10
|
+
'</head>': {
|
|
11
|
+
before: "{% hook 'head-end' %}",
|
|
12
|
+
},
|
|
13
|
+
'<body>': {
|
|
14
|
+
after: "{% hook 'body-start' %}",
|
|
15
|
+
},
|
|
16
|
+
'</body>': {
|
|
17
|
+
before: "{% hook 'body-end' %}",
|
|
18
|
+
},
|
|
19
|
+
'<header>': {
|
|
20
|
+
after: "{% hook 'header-start' %}",
|
|
21
|
+
},
|
|
22
|
+
'</header>': {
|
|
23
|
+
before: "{% hook 'header-end' %}",
|
|
24
|
+
},
|
|
25
|
+
'<main>': {
|
|
26
|
+
before: "{% hook 'before-main' %}",
|
|
27
|
+
after: "{% hook 'main-start' %}",
|
|
28
|
+
},
|
|
29
|
+
'</main>': {
|
|
30
|
+
before: "{% hook 'main-end' %}",
|
|
31
|
+
after: "{% hook 'after-main' %}",
|
|
32
|
+
},
|
|
33
|
+
'<footer>': {
|
|
34
|
+
before: "{% hook 'before-footer' %}",
|
|
35
|
+
after: "{% hook 'footer-start' %}",
|
|
36
|
+
},
|
|
37
|
+
'</footer>': {
|
|
38
|
+
before: "{% hook 'footer-end' %}",
|
|
39
|
+
after: "{% hook 'after-footer' %}",
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = function() {
|
|
44
|
+
const themeLiquid = path.resolve(ThemeEnvy.themePath, 'layout/theme.liquid')
|
|
45
|
+
if (fs.existsSync(themeLiquid)) {
|
|
46
|
+
const theme = fs.readFileSync(themeLiquid, 'utf8')
|
|
47
|
+
let source = theme
|
|
48
|
+
Object.entries(hooks).forEach(([tag, { after, before }]) => {
|
|
49
|
+
const type = tag.includes('/') ? 'closing' : 'opening'
|
|
50
|
+
const tagName = type === 'closing' ? tag.replace('</', '').replace('>', '') : tag.replace('<', '').replace('>', '')
|
|
51
|
+
// regexp for tag
|
|
52
|
+
const checkTag = type === 'closing' ? new RegExp(`</${tagName}>`, 'gm') : new RegExp(`<${tagName}[^>]*>`, 'gm')
|
|
53
|
+
const matches = source.match(checkTag)
|
|
54
|
+
if (matches) {
|
|
55
|
+
// insert before and after attributes around the tag matches
|
|
56
|
+
matches.forEach(match => {
|
|
57
|
+
let newMatch = match
|
|
58
|
+
if (after && !source.includes(after)) newMatch = newMatch.replace('>', `>\n${after}`)
|
|
59
|
+
if (before && !source.includes(before)) newMatch = newMatch.replace('<', `${before}\n<`)
|
|
60
|
+
source = source.replace(match, newMatch)
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
source = liquidPrettify({ source, pathname: themeLiquid })
|
|
65
|
+
fs.writeFileSync(themeLiquid, source, 'utf8')
|
|
66
|
+
}
|
|
67
|
+
console.log(`${logSymbols.success} Hooks installed\n`)
|
|
68
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const fs = require('fs-extra')
|
|
3
|
+
const logSymbols = require('#LogSymbols')
|
|
4
|
+
|
|
5
|
+
module.exports = function({ sourceTheme }) {
|
|
6
|
+
// Set config/settings_schema to .js if settings_schema is json
|
|
7
|
+
const settingsSchema = path.resolve(sourceTheme, 'config/settings_schema.json')
|
|
8
|
+
if (fs.existsSync(settingsSchema)) {
|
|
9
|
+
// rename settings_schema.json to settings_schema.js
|
|
10
|
+
fs.writeFileSync(path.resolve(sourceTheme, 'config/settings_schema.json'), `module.exports = ${JSON.stringify(require(settingsSchema), null, 2)}`)
|
|
11
|
+
fs.renameSync(settingsSchema, path.resolve(sourceTheme, 'config/settings_schema.js'))
|
|
12
|
+
}
|
|
13
|
+
console.log(`${logSymbols.success} settings_schema converted to JS`)
|
|
14
|
+
}
|
package/convert/index.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/*
|
|
2
|
+
theme-envy convert --(source|src|S)=path/to/theme
|
|
3
|
+
|
|
4
|
+
converts a Shopify theme to Theme Envy directory structure
|
|
5
|
+
- should be run after theme-envy-import, by using the --convert flag
|
|
6
|
+
- can be run independently, node bin/theme-envy convert --source=path/to/theme, or --src=path/to/theme, or -S=path/to/theme
|
|
7
|
+
*/
|
|
8
|
+
const path = require('path')
|
|
9
|
+
const fs = require('fs-extra')
|
|
10
|
+
const chalk = require('chalk')
|
|
11
|
+
const logSymbols = require('#LogSymbols')
|
|
12
|
+
const { directories, ensureDirectories } = require('#EnsureDirectories')
|
|
13
|
+
const { setSettingsSchemaJs, convertSectionsToFeatures, installHooks } = require('#Convert/functions')
|
|
14
|
+
const { addThemeEnvyFeatures } = require('#Init/functions')
|
|
15
|
+
|
|
16
|
+
module.exports = async function(src, opts = {}) {
|
|
17
|
+
console.log(`\n ${logSymbols.info} ${chalk.cyan('Converting theme structure...')}`)
|
|
18
|
+
const source = src || './src'
|
|
19
|
+
const sourceTheme = path.resolve(process.cwd(), source)
|
|
20
|
+
// verify source theme exists
|
|
21
|
+
if (!fs.existsSync(sourceTheme)) {
|
|
22
|
+
console.error(
|
|
23
|
+
logSymbols.error,
|
|
24
|
+
chalk.red.bold('Error:'),
|
|
25
|
+
`Source theme directory not found: ${sourceTheme}`
|
|
26
|
+
)
|
|
27
|
+
process.exit(1)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// validate directory structure of source theme
|
|
31
|
+
directories.forEach(dir => {
|
|
32
|
+
if (!fs.existsSync(path.resolve(sourceTheme, dir))) {
|
|
33
|
+
console.error(`${logSymbols.error} ${chalk.red.bold('Error:')} Source theme required directory not found: ${path.resolve(sourceTheme, dir)}`)
|
|
34
|
+
process.exit(1)
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// Create theme-envy directories
|
|
39
|
+
ensureDirectories({ root: sourceTheme, envy: true })
|
|
40
|
+
console.log(`${logSymbols.success} ${chalk.green('theme-envy')} directories created`)
|
|
41
|
+
|
|
42
|
+
if (opts.addThemeEnvy !== false) addThemeEnvyFeatures({ dest: sourceTheme })
|
|
43
|
+
|
|
44
|
+
convertSectionsToFeatures({ sourceTheme })
|
|
45
|
+
|
|
46
|
+
installHooks()
|
|
47
|
+
|
|
48
|
+
// convert settings_schema.json to .js
|
|
49
|
+
if (fs.existsSync(path.join(source, 'config/settings_schema.json'))) setSettingsSchemaJs({ sourceTheme })
|
|
50
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description theme-envy dev starts development process and syncs with Shopify using the Shopify CLI
|
|
3
|
+
* @example npx theme-envy dev
|
|
4
|
+
* @returns {Void}
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { spawn } = require('child_process')
|
|
8
|
+
const path = require('path')
|
|
9
|
+
|
|
10
|
+
module.exports = function() {
|
|
11
|
+
const relativeDistPath = path.relative(process.cwd(), ThemeEnvy.outputPath)
|
|
12
|
+
const shopifyDev = `shopify theme dev --store=${ThemeEnvy.store} --path=${relativeDistPath}`
|
|
13
|
+
const build = 'theme-envy build development --watch'
|
|
14
|
+
spawn('stmux', ['-w', 'always', '-e', 'ERROR', '-M', '-m', 'beep,system', '--', '[', '[', shopifyDev, '..', build, ']', ']'], { stdio: 'inherit' })
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Deletes and rebuilds the output directory before a build
|
|
3
|
+
* @param {Object} options
|
|
4
|
+
* @param {boolean} options.quiet - Do not log the clean message
|
|
5
|
+
* @returns {Void}
|
|
6
|
+
* @example distClean()
|
|
7
|
+
* @example distClean({ quiet: true })
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const path = require('path')
|
|
11
|
+
const fs = require('fs-extra')
|
|
12
|
+
const emoji = require('node-emoji')
|
|
13
|
+
|
|
14
|
+
module.exports = function({ quiet } = {}) {
|
|
15
|
+
fs.removeSync(ThemeEnvy.outputPath)
|
|
16
|
+
fs.mkdirSync(ThemeEnvy.outputPath)
|
|
17
|
+
const relativeDistPath = path.relative(process.cwd(), ThemeEnvy.outputPath)
|
|
18
|
+
if (!quiet) console.log(emoji.get('sparkles'), `./${relativeDistPath} directory cleaned`)
|
|
19
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description Exports a function that ensures our directory structure is in place.
|
|
3
|
+
* @param {string} root - The root directory to ensure the directories are in place.
|
|
4
|
+
* @param {boolean} envy - Whether or not to ensure the theme-envy specific directories.
|
|
5
|
+
* @example
|
|
6
|
+
* ensureDirectories({ root: ThemeEnvy.themePath, envy: true })
|
|
7
|
+
* @returns {Void}
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs-extra')
|
|
11
|
+
const path = require('path')
|
|
12
|
+
|
|
13
|
+
const directories = ['assets', 'config', 'layout', 'locales', 'sections', 'snippets', 'templates']
|
|
14
|
+
const envyDirectories = ['theme-envy/elements', 'theme-envy/features', 'theme-envy/partials', 'theme-envy/schema']
|
|
15
|
+
function ensureDirectory(root, dir) {
|
|
16
|
+
fs.ensureDirSync(path.resolve(root, dir))
|
|
17
|
+
}
|
|
18
|
+
module.exports = {
|
|
19
|
+
directories,
|
|
20
|
+
envyDirectories,
|
|
21
|
+
ensureDirectories({ root = ThemeEnvy.themePath, envy = false }) {
|
|
22
|
+
directories.forEach(dir => ensureDirectory(root, dir))
|
|
23
|
+
if (!envy) return
|
|
24
|
+
envyDirectories.forEach(dir => ensureDirectory(root, dir))
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file finds orphaned snippets, partials, and assets that have no references in the theme
|
|
3
|
+
*/
|
|
4
|
+
const chalk = require('chalk')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const { dependencies } = require('./liquid-tree/objects')
|
|
7
|
+
const { countResults } = require('./liquid-tree/functions')
|
|
8
|
+
const getAll = require('#Build/functions/get-all.js')
|
|
9
|
+
|
|
10
|
+
module.exports = () => {
|
|
11
|
+
const files = [...getAll('snippets'), ...getAll('partials'), ...getAll('assets')]
|
|
12
|
+
const results = files.filter(file => countResults(dependencies.check(file)) <= 1)
|
|
13
|
+
if (!results.length) {
|
|
14
|
+
console.log('No orphaned snippets, partials, or assets found.')
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
console.log(chalk.yellow.bold('Orphaned files found:'))
|
|
19
|
+
results.forEach(file => console.log(`${chalk.red('*')} ${path.relative(ThemeEnvy.themePath, file)}`))
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Establishes our global ThemeEnvy object based on theme.config.js
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const fs = require('fs-extra')
|
|
7
|
+
const EventEmitter = require('events')
|
|
8
|
+
|
|
9
|
+
const themeConfigPath = path.resolve(process.cwd(), 'theme.config.js')
|
|
10
|
+
global.ThemeEnvy = fs.existsSync(themeConfigPath) ? require(themeConfigPath) : {}
|
|
11
|
+
|
|
12
|
+
ThemeEnvy.dependencies = {}
|
|
13
|
+
ThemeEnvy.events = new EventEmitter()
|
|
14
|
+
ThemeEnvy.themePath = path.resolve(process.cwd(), (ThemeEnvy.themePath || 'src'))
|
|
15
|
+
ThemeEnvy.outputPath = path.resolve(process.cwd(), (ThemeEnvy.outputPath || 'dist'))
|
|
16
|
+
|
|
17
|
+
ThemeEnvy.paths = {
|
|
18
|
+
build: path.resolve(__dirname, '../../build'),
|
|
19
|
+
helpers: path.resolve(__dirname, '../../helpers'),
|
|
20
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Attempts to run prettier-plugin-liquid on source string
|
|
3
|
+
* @param {Object} options
|
|
4
|
+
* @param {string} options.source - The source string to prettify
|
|
5
|
+
* @param {string} options.pathname - The pathname of the file being prettified
|
|
6
|
+
* @param {boolean} options.verbose - Whether or not to log warnings
|
|
7
|
+
* @returns {string} - The prettified source string or the original source string if prettier fails
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const liquidPlugin = require('@shopify/prettier-plugin-liquid/standalone')
|
|
11
|
+
const prettier = require('prettier/standalone')
|
|
12
|
+
const chalk = require('chalk')
|
|
13
|
+
const logSymbols = require('#LogSymbols')
|
|
14
|
+
|
|
15
|
+
module.exports = function({ source, pathname, verbose }) {
|
|
16
|
+
let prettified = false
|
|
17
|
+
try {
|
|
18
|
+
prettified = prettier.format(source, { plugins: [liquidPlugin], parser: 'liquid-html' })
|
|
19
|
+
} catch (error) {
|
|
20
|
+
if (verbose) console.log(logSymbols.warning, chalk.yellow('Warning:'), chalk.dim(`there was an issue prettifying ${pathname}`))
|
|
21
|
+
}
|
|
22
|
+
if (prettified) return prettified
|
|
23
|
+
return source
|
|
24
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const countResults = (obj) => {
|
|
2
|
+
// deep count number of entries in results object
|
|
3
|
+
let count = 0
|
|
4
|
+
Object.keys(obj).forEach(key => {
|
|
5
|
+
if (obj[key].tree) {
|
|
6
|
+
count += countResults(obj[key].tree)
|
|
7
|
+
}
|
|
8
|
+
count++
|
|
9
|
+
})
|
|
10
|
+
return count
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = countResults
|