@softlimit/theme-envy 0.1.2-alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. package/.eslintrc.js +26 -0
  2. package/.github/CODEOWNERS +1 -0
  3. package/.github/workflows/release-please.yml +19 -0
  4. package/LICENSE +21 -0
  5. package/README.md +259 -0
  6. package/build/functions/failed-hook-installs.js +18 -0
  7. package/build/functions/get-all.js +102 -0
  8. package/build/functions/index.js +7 -0
  9. package/build/functions/liquid/functions/extend-liquid.js +77 -0
  10. package/build/functions/liquid/functions/flatten-shopify-directory-structure.js +29 -0
  11. package/build/functions/liquid/functions/index.js +6 -0
  12. package/build/functions/liquid/functions/list-dependencies.js +47 -0
  13. package/build/functions/liquid/functions/section-schema-inject.js +26 -0
  14. package/build/functions/liquid/index.js +36 -0
  15. package/build/functions/parent-theme-files.js +25 -0
  16. package/build/functions/tailwind.js +31 -0
  17. package/build/functions/theme-envy.js +85 -0
  18. package/build/functions/webpack.js +44 -0
  19. package/build/index.js +45 -0
  20. package/build/requires/assets.js +22 -0
  21. package/build/requires/config.js +44 -0
  22. package/build/requires/globals/index.js +10 -0
  23. package/build/requires/globals/parent-theme.js +28 -0
  24. package/build/requires/globals/prepare-install-hooks-schema.js +58 -0
  25. package/build/requires/globals/progress-bar.js +52 -0
  26. package/build/requires/globals/theme-require.js +190 -0
  27. package/build/requires/index.js +21 -0
  28. package/build/requires/locales.js +16 -0
  29. package/build/requires/scripts/index.js +5 -0
  30. package/build/requires/scripts/public-path.js +9 -0
  31. package/build/requires/scripts/script-builders/elements.build.js +44 -0
  32. package/build/requires/scripts/script-builders/features.build.js +15 -0
  33. package/build/requires/scripts/theme-envy.js +7 -0
  34. package/build/requires/snippets/index.js +11 -0
  35. package/build/requires/snippets/liquid-builders/theme-envy.liquid.build.js +21 -0
  36. package/build/requires/styles/index.js +1 -0
  37. package/build/requires/styles/styles-builders/theme-envy.css.js +11 -0
  38. package/build/requires/styles/tailwind-base.css +3 -0
  39. package/build/requires/templates.js +20 -0
  40. package/build/theme-envy.config.js +71 -0
  41. package/convert/functions/convert-sections-to-features.js +56 -0
  42. package/convert/functions/detect-children.js +30 -0
  43. package/convert/functions/index.js +6 -0
  44. package/convert/functions/install-hooks.js +68 -0
  45. package/convert/functions/set-settings-schema-js.js +14 -0
  46. package/convert/index.js +50 -0
  47. package/helpers/functions/dev.js +15 -0
  48. package/helpers/functions/dist-clean.js +19 -0
  49. package/helpers/functions/ensure-directories.js +26 -0
  50. package/helpers/functions/find-orphans.js +20 -0
  51. package/helpers/functions/global-theme-envy.js +20 -0
  52. package/helpers/functions/liquid-prettify.js +24 -0
  53. package/helpers/functions/liquid-tree/functions/count-results.js +13 -0
  54. package/helpers/functions/liquid-tree/functions/get-depth.js +12 -0
  55. package/helpers/functions/liquid-tree/functions/get-file-info.js +11 -0
  56. package/helpers/functions/liquid-tree/functions/index.js +5 -0
  57. package/helpers/functions/liquid-tree/index.js +48 -0
  58. package/helpers/functions/liquid-tree/objects/dependencies.js +74 -0
  59. package/helpers/functions/liquid-tree/objects/index.js +3 -0
  60. package/helpers/functions/log-symbols.js +28 -0
  61. package/helpers/functions/pull-json.js +22 -0
  62. package/helpers/functions/scaffold-new/functions/element.js +15 -0
  63. package/helpers/functions/scaffold-new/functions/feature.js +76 -0
  64. package/helpers/functions/scaffold-new/functions/index.js +6 -0
  65. package/helpers/functions/scaffold-new/functions/load-dir.js +24 -0
  66. package/helpers/functions/scaffold-new/functions/starter-content.js +21 -0
  67. package/helpers/functions/scaffold-new/functions/upper-first-letter.js +3 -0
  68. package/helpers/functions/scaffold-new/index.js +28 -0
  69. package/helpers/functions/scaffold-new/objects/index.js +3 -0
  70. package/helpers/functions/scaffold-new/objects/starter-configs.js +7 -0
  71. package/helpers/functions/unicode-supported.js +21 -0
  72. package/helpers/index.js +10 -0
  73. package/index.js +190 -0
  74. package/init/functions/add-theme-envy-features/features/theme-envy/install.js +6 -0
  75. package/init/functions/add-theme-envy-features/index.js +21 -0
  76. package/init/functions/copy-example-feature/example-feature/config/_example-feature.js +8 -0
  77. package/init/functions/copy-example-feature/example-feature/index.js +5 -0
  78. package/init/functions/copy-example-feature/example-feature/install.js +10 -0
  79. package/init/functions/copy-example-feature/example-feature/partials/_example-feature-partial.liquid +3 -0
  80. package/init/functions/copy-example-feature/example-feature/schema/schema-example-feature-schema-partial.js +16 -0
  81. package/init/functions/copy-example-feature/example-feature/schema/schema-example-feature-section.js +14 -0
  82. package/init/functions/copy-example-feature/example-feature/scripts/example-feature.js +3 -0
  83. package/init/functions/copy-example-feature/example-feature/sections/example-feature-section.liquid +11 -0
  84. package/init/functions/copy-example-feature/example-feature/snippets/example-feature-snippet.liquid +1 -0
  85. package/init/functions/copy-example-feature/index.js +25 -0
  86. package/init/functions/copy-starter-config-files/configs/postcss.config.js +9 -0
  87. package/init/functions/copy-starter-config-files/configs/tailwind.config.js +16 -0
  88. package/init/functions/copy-starter-config-files/configs/theme.config.js +25 -0
  89. package/init/functions/copy-starter-config-files/index.js +28 -0
  90. package/init/functions/copy-starter-config-files/utils/starter-config.js +17 -0
  91. package/init/functions/copy-starter-config-files/utils/starter-element.js +16 -0
  92. package/init/functions/copy-starter-config-files/utils/starter-install.js +14 -0
  93. package/init/functions/copy-starter-config-files/utils/starter-schema.js +32 -0
  94. package/init/functions/copy-starter-config-files/utils/starter-section.js +16 -0
  95. package/init/functions/create-empty-settings-data.js +19 -0
  96. package/init/functions/create-settings-schema.js +29 -0
  97. package/init/functions/if-shopify-theme-exists.js +26 -0
  98. package/init/functions/import-from-git.js +21 -0
  99. package/init/functions/index.js +10 -0
  100. package/init/functions/validate-source-theme.js +28 -0
  101. package/init/index.js +87 -0
  102. package/package.json +88 -0
package/.eslintrc.js ADDED
@@ -0,0 +1,26 @@
1
+ module.exports = {
2
+ env: {
3
+ browser: true,
4
+ es2021: true
5
+ },
6
+ extends: [
7
+ 'standard'
8
+ ],
9
+ globals: {
10
+ ThemeRequire: true,
11
+ ThemeEnvy: true,
12
+ },
13
+ parserOptions: {
14
+ ecmaVersion: 2021,
15
+ sourceType: 'module'
16
+ },
17
+ rules: {
18
+ 'prefer-const': 1,
19
+ 'comma-dangle': 0,
20
+ 'space-before-function-paren': ['error', {
21
+ anonymous: 'never',
22
+ named: 'never',
23
+ asyncArrow: 'always'
24
+ }]
25
+ }
26
+ }
@@ -0,0 +1 @@
1
+ * @calebcurtis8 @itsanolive
@@ -0,0 +1,19 @@
1
+ on:
2
+ push:
3
+ branches:
4
+ - main
5
+
6
+ permissions:
7
+ contents: write
8
+ pull-requests: write
9
+
10
+ name: release-please
11
+
12
+ jobs:
13
+ release-please:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: google-github-actions/release-please-action@v3
17
+ with:
18
+ release-type: node
19
+ package-name: release-please-action
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Softlimit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,259 @@
1
+ <!-- omit in toc -->
2
+ # 💚 Theme Envy for Shopify Themes 💚
3
+
4
+ <!-- omit in toc -->
5
+ ## _The lean, mean development machine for your Shopify themes_
6
+
7
+ [![Built by Softlimit](https://cdn.shopify.com/s/files/1/0581/4760/2587/files/softlimit-app-icons-03.png?v=1680017399)](https://www.softlimit.com/)
8
+
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
+ <!-- omit in toc -->
11
+ ## Theme Envy Features
12
+
13
+ - **Get to work**:
14
+ Use [`theme-envy`](#theme-envy-cli) CLI commands to build, serve, initialize, add new feature structure, and much more, without touching your package.json. ⚡ **Bonus**: *Theme Envy's build and watch processes are super fast.* ⚡
15
+ - **Stay organized**:
16
+ Put related files together in their own [feature subdirectories](#features) instead of the default Shopify theme structure. You can also add subdirectories within the default Shopify `/snippets` and `/sections` folders to group files.
17
+ - **Repeat yourself**:
18
+ Break up code into smaller, reusable pieces with Theme Envy's extended liquid [partials](#partials) and [schema](#schematheme-require) files.
19
+ - **Integrate with Ease**:
20
+ Manage app and feature implementation using Theme Envy's [`hooks`](#hooksinstalls) and [`installs`](#hooksinstalls) system.
21
+ - **Code in Style**:
22
+ Nest custom styling or apply classes with PostCSS and [Tailwind](#tailwind) built in.
23
+ - **Stay Consistent**:
24
+ Customize new feature starter files with your own markup and settings to establish consistency in your theme code base.
25
+ - **Customize your Configuration**:
26
+ Add and update values in your `theme.config.js` for direct reference in liquid files using the [`{% theme %}`](#theme) tag
27
+ - **Optimize Performance**:
28
+ Automatically lazy-load custom JS elements only when they are present on the page and use [Webpack](#webpack) dynamic imports to conditionally load assets.
29
+ - **Build for Production**:
30
+ Automatically prettify liquid and minify javascript on production builds.
31
+
32
+ ---
33
+
34
+ Table of Contents
35
+ - [Installation](#installation)
36
+ - [Getting Started](#getting-started)
37
+ - [Theme Directory Structure](#theme-directory-structure)
38
+ - [CLI](#cli)
39
+ - [Build Tools](#build-tools)
40
+ - [Elements](#elements)
41
+ - [Features](#features)
42
+ - [Hooks/installs](#hooksinstalls)
43
+ - [Critical CSS](#critical-css)
44
+ - [Schema/Theme Require](#schematheme-require)
45
+ - [JS Section Schema](#js-section-schema)
46
+ - [Partials](#partials)
47
+ - [Theme](#theme)
48
+ - [Theme.config.js](#themeconfigjs)
49
+ - [Tailwind](#tailwind)
50
+ - [Webpack](#webpack)
51
+ - [Contributing](#contributing)
52
+ - [License](#license)
53
+
54
+ ## Installation
55
+
56
+ Use [NPM](https://www.npm.js) to install Theme Envy.
57
+
58
+ ```bash
59
+ npm install @softlimit/theme-envy --save-dev
60
+ ```
61
+
62
+ ## Getting Started
63
+ Get started by running the following command and answering the prompts:
64
+ ```bash
65
+ npx theme-envy init
66
+ ```
67
+
68
+ > **Note**
69
+ > During the `init` command a new "Feature" is added to your project called `theme-envy` in `src/theme-envy/features/theme-envy`. This feature will add a snippet and `{% render 'theme-envy' %}` to the end of your `<head>` tag in `layout/theme.liquid` during project build. This handles all the Theme Envy JS and CSS.
70
+
71
+ You are now ready to start developing! Get started with this simple command in your terminal
72
+ ```bash
73
+ npx theme-envy dev
74
+ ```
75
+
76
+ ## Theme Directory Structure
77
+ The directories are [Shopify standard directories](https://shopify.dev/docs/themes/architecture#directory-structure-and-component-types) except for `theme-envy`.
78
+ ```bash
79
+ /
80
+ └── assets/
81
+ └── config/
82
+ └── layout/
83
+ └── locales/
84
+ └── sections/
85
+ └── snippets/
86
+ └── templates/
87
+ └── customers/
88
+ └── theme-envy/
89
+ └── elements/ # for Custom Elements/Web Components
90
+ └── features/ # individual directories of discrete features
91
+ └── partials/ # small .liquid files that are reusable/inserted across the theme
92
+ └── schema/ # .js files for sharing bits of section/config schema across the theme
93
+ ```
94
+
95
+ ## CLI
96
+ > **Note**
97
+ > Precede all commands with `npx theme-envy`
98
+ ```
99
+ Usage: theme-envy [options] [command]
100
+
101
+ Theme Envy CLI Tools
102
+
103
+ Options:
104
+ -h, --help display help for command
105
+
106
+ Commands:
107
+ build [options] [env] Build Shopify theme
108
+ clean Empty output directory
109
+ convert [options] [source] Convert an existing Shopify theme to Theme Envy directory structure
110
+ dev Start development process and sync with Shopify using the Shopify CLI
111
+ find-orphans Find unused snippets, partials, and assets in your Shopify theme
112
+ init [options] [source] Initialize a new Shopify theme project with Theme Envy directory structure
113
+ new <type> <name> [include] Create new Feature or Element from starter files
114
+ pull-json Pull json template, section, and settings_data files from theme using Shopify CLI
115
+ tree [options] [filepaths] Display the dependency tree for a .liquid file
116
+ help [command] display help for command
117
+ ```
118
+
119
+ ***
120
+ ## Build Tools
121
+
122
+ ### Elements
123
+ Theme Envy is optimized for using [Web Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components). You can initialize a new Web Component using `utils/starter-element.js` in your project with this command in your terminal:
124
+ ```bash
125
+ npx theme-envy new element <your-element>
126
+ ```
127
+ Your **element** file must have the same name as your new CustomElement (Web Component). Theme Envy will only load the asset(s) for `your-element` if element is present in the DOM.
128
+
129
+ > **Warning**
130
+ > To take advantage of Theme Envy's smart loading of **elements** they must be inside an `elements` directory
131
+
132
+ You can also setup an **element** as a subdirectory of `theme-envy/elements` by using an index.js file (`theme-envy/elements/your-element/index.js`). In this case your subdirectory must have the same name as your Web Component.
133
+
134
+ ***
135
+ ### Features
136
+ Theme Envy "Features" are bigger pieces/sections of your site. Any JS/CSS assets associated with a feature will be loaded as part of the main `theme-envy.js` file on all templates in your theme.
137
+
138
+ > **Note**
139
+ > Features are subdirectories of `src/theme-envy/features`.
140
+
141
+ ```bash
142
+ /
143
+ └── theme-envy/features
144
+ └── your-feature/
145
+ └── config/ # .js files concatenated and added to settings_schema.json
146
+ └── partials/ # .liquid files that are referenced using Theme Envy {% partial 'file-name' %} tag
147
+ └── schema/ # .js files with module.exports to be injected into section files, or referenced with ThemeRequire()
148
+ └── scripts/ # .js files to be imported into index.js
149
+ └── sections/ # .liquid files only, included in build automatically
150
+ └── snippets/ # .liquid files only, included in build automatically
151
+ └── styles/ # contains any .css files, must be imported into index.js
152
+ └── critical.css # optional file that adds render blocking, sitewide CSS
153
+ └── index.js # is concatenated and loaded sitewide
154
+ └── install.js # defines where to inject code into hooks
155
+ ```
156
+
157
+ ***
158
+ ### Hooks/installs
159
+ Hooks are places in the theme code where *Features* and *Elements* can insert code during build. This allows us to keep integrations discrete and easily undoable. To define a hook in the theme, we only need to add a `hook` tag like so:
160
+ ```javascript
161
+ {% hook 'head-end' %}
162
+ ```
163
+ We can then reference this hook's location with an install.js file in a feature or element.
164
+ ```javascript
165
+ module.exports = [
166
+ {
167
+ hook: 'head-end',
168
+ content: "{% render 'custom-snippet' %}",
169
+ priority: 0-100 // optional, 50 is default
170
+ }
171
+ ]
172
+ ```
173
+ During the build, all code for the `head-end` hook will be collected, sorted by priority (where 0 is highest in the code), and will replace the `{% hook 'head-end' %}` tag.
174
+ ***
175
+ ### Critical CSS
176
+ In "Features" you may write render blocking CSS that you want to load site wide in a `critical.css` file in the Feature's directory. The critical.css files are concatenated with the Tailwind Stylesheet (when enabled), so that they are render blocking, loaded once, and then cached for subsequent page loads.
177
+ ***
178
+ ### Schema/Theme Require
179
+ We were frustrated by how difficult it is to share smaller pieces of Shopify Section schema across sections. Theme Envy includes a couple of tools to make this easier.
180
+ ***
181
+ ### JS Section Schema
182
+ Use this syntax in section `.liquid` files in place of the normal `{% schema %}{% endschema %}` tags:
183
+ ```javascript
184
+ {% schema 'schema-your-section.js' %}
185
+ ```
186
+ > **Note**
187
+ > Don't worry about relative/absolute paths here, Theme Envy will find your uniquely named schema js file within your project.
188
+
189
+ When managing your section as a **Feature** we recommend putting all of your schema files in that feature's `schema` subdirectory. Schema that is shared across multiple features/sections should go in `src/theme-envy/schema`
190
+ > **Warning**
191
+ > All **schema** files must be within a `schema` directory
192
+
193
+ ***
194
+ ### Partials
195
+ By using the syntax
196
+ ```javascript
197
+ {% partial '_file-name' %}
198
+ ```
199
+ the contents of the liquid file named `_file-name.liquid` are inserted directly into the output file. Our practice is to name these files with a leading _, but it is not required.
200
+ > **Warning**
201
+ > All **partial** files must be within a `partials` directory
202
+
203
+ ***
204
+ ### Theme
205
+ We can use this markup to access properties of the `theme.config.js` file within liquid files. This is especially helpful for when you have to access a breakpoint value within your markup.
206
+ ```javascript
207
+ {% theme 'breakpoints.md' %} // defined in theme.config.js
208
+ ```
209
+
210
+ ***
211
+ ## Theme.config.js
212
+ After initializing your Theme Envy project, you will find a `theme.config.js` file in your project root. This is where we manage all of the Theme Envy build options.
213
+
214
+ ```javascript
215
+ module.exports = {
216
+ entry: {
217
+ // Add entrypoints (Webpack) here
218
+ // main: './src/scripts/main.js',
219
+ },
220
+ store: 'sl-dev-testing.myshopify.com',
221
+ themePath: 'src', // directory for theme source files
222
+ outputPath: 'dist', // directory for build output
223
+ // tailwind: true, // set to false to disable Tailwind
224
+ breakpoints: {
225
+ sm: '640px',
226
+ // => @media (min-width: 640px) { ... }
227
+
228
+ md: '768px',
229
+ // => @media (min-width: 768px) { ... }
230
+
231
+ lg: '1024px',
232
+ // => @media (min-width: 1024px) { ... }
233
+
234
+ xl: '1280px',
235
+ // => @media (min-width: 1280px) { ... }
236
+
237
+ '2xl': '1536px'
238
+ // => @media (min-width: 1536px) { ... }
239
+ }
240
+ }
241
+ ```
242
+
243
+ ***
244
+ ## Tailwind
245
+ [Tailwind](https://tailwindcss.com/) is enabled automatically. It can be disabled in `theme.config.js`, and customized in `tailwind.config.js`
246
+ ***
247
+ ## Webpack
248
+ [Webpack](https://webpack.js.org/) is used to bundle and manage JS and CSS. By default, the only entry point is Theme Envy which will handle all `elements` and `features`. Any additional scripts or stylesheets can be added to the `entry` in `theme.config.js`.
249
+
250
+ ***
251
+ ## Contributing
252
+
253
+ Pull requests are welcome. For major changes, please open an issue first
254
+ to discuss what you would like to change.
255
+
256
+ ***
257
+ ## License
258
+
259
+ [MIT](https://choosealicense.com/licenses/mit/)
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @private
3
+ * @file checks for failed hook installs and exits with an error
4
+ */
5
+
6
+ const chalk = require('chalk')
7
+ const logSymbols = require('#LogSymbols')
8
+
9
+ module.exports = function() {
10
+ const unusedHookInstalls = Object.keys(ThemeEnvy.hooks).filter(hook => !ThemeEnvy.hooks[hook].replaced)
11
+ if (!unusedHookInstalls || unusedHookInstalls.length === 0) return
12
+ console.error(`
13
+ ${logSymbols.error} ${chalk.red.bold('Error:')}
14
+ The following hook injections failed because there is no corresponding hook tag in the theme:
15
+ ${chalk.red(unusedHookInstalls.join('\n'))}
16
+ `)
17
+ process.exit()
18
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * @file Gets all files of a given type
3
+ * @param {string} type - The type of files to get
4
+ * @example
5
+ * // get all liquid files
6
+ * getAll('liquid')
7
+ * @example
8
+ * // get all config files
9
+ * getAll('config')
10
+ * @returns {array} - array of file paths
11
+ */
12
+ const path = require('path')
13
+ const glob = require('glob')
14
+ const parentThemeFiles = require('./parent-theme-files')
15
+
16
+ const globs = {
17
+ assets: {
18
+ glob(src) {
19
+ return glob.sync(path.resolve(src, '**/assets/**/*'))
20
+ },
21
+ },
22
+ config: {
23
+ glob(src) {
24
+ return glob.sync(path.resolve(src, '**/config/**/*.js'))
25
+ },
26
+ filter: file => path.basename(file) !== 'settings_schema.js',
27
+ },
28
+ criticalCSS: {
29
+ glob(src) {
30
+ return glob.sync(path.resolve(src, '**/critical.css'))
31
+ },
32
+ },
33
+ elements: {
34
+ glob(src) {
35
+ return [...glob.sync(path.resolve(src, '**/elements/**/index.js')), ...glob.sync(path.resolve(src, '**/elements/*.js'))]
36
+ }
37
+ },
38
+ features: {
39
+ glob(src) {
40
+ return glob.sync(path.resolve(src, 'theme-envy/features/**/index.js'))
41
+ },
42
+ },
43
+ installs: {
44
+ glob(src) {
45
+ return glob.sync(path.resolve(src, '**/install.js'))
46
+ },
47
+ },
48
+ liquid: {
49
+ glob(src) {
50
+ return glob.sync(path.resolve(src, '**/*.liquid'))
51
+ },
52
+ filter: file => !file.includes('partials'),
53
+ },
54
+ partials: {
55
+ glob(src) {
56
+ return glob.sync(path.resolve(src, '**/partials/**/*.liquid'))
57
+ },
58
+ },
59
+ schema: {
60
+ glob(src) {
61
+ return glob.sync(path.resolve(src, '**/schema/**/*.js'))
62
+ },
63
+ },
64
+ sectionGroups: {
65
+ glob(src) {
66
+ return glob.sync(path.resolve(src, '**/sections/**/*.json'))
67
+ },
68
+ },
69
+ snippets: {
70
+ glob(src) {
71
+ return glob.sync(path.resolve(src, '**/snippets/**/*.liquid'))
72
+ },
73
+ },
74
+ templates: {
75
+ glob(src) {
76
+ return glob.sync(path.resolve(src, '**/templates/**/*.json'))
77
+ },
78
+ },
79
+ }
80
+
81
+ module.exports = function(type) {
82
+ function getFiles(src, only) {
83
+ // src is either the themePath or the parentTheme
84
+ // only is a list of directory names to filter against, used for parentTheme
85
+ let files = globs[type].glob(src)
86
+ if (only) {
87
+ files = files.filter(file => {
88
+ return only.some(dir => file.indexOf(dir) > -1)
89
+ })
90
+ }
91
+ if (globs[type].filter) {
92
+ files = files.filter(globs[type].filter)
93
+ }
94
+ return files
95
+ }
96
+ const files = getFiles(ThemeEnvy.themePath)
97
+ if (ThemeEnvy.parentTheme) {
98
+ files.push(...parentThemeFiles(getFiles, files, type))
99
+ }
100
+
101
+ return files
102
+ }
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ getAll: require('./get-all'),
3
+ liquid: require('./liquid'),
4
+ tailwind: require('./tailwind'),
5
+ themeEnvy: require('./theme-envy'),
6
+ webpack: require('./webpack'),
7
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @file Pre-processes .liquid files to allow for our custom tags: partial, hook, and theme
3
+ * @example
4
+ * // partial tag copies the contents of _cart-item-title.liquid
5
+ * {% partial '_cart-item-title' %}
6
+ * @example
7
+ * // adds a hook (point) to the liquid file that can be injected to from install.js files
8
+ * {% hook 'cart-item-title' %}
9
+ * @example
10
+ * // references any object in the theme.config.js file
11
+ * {% theme 'breakpoints.md' %}
12
+ */
13
+
14
+ const path = require('path')
15
+ const fs = require('fs-extra')
16
+ const listDependencies = require('./list-dependencies')
17
+ const getAll = require('#Build/functions/get-all.js')
18
+ const globbedPartials = getAll('partials')
19
+
20
+ const strings = {
21
+ tags: ['partial', 'hook', 'theme']
22
+ }
23
+ // build list of strings to test for replacing
24
+ strings.test = strings.tags.map(tag => [`${tag} '`, `${tag} "`]).flat()
25
+
26
+ const extendLiquid = ({ source, filePath }) => {
27
+ // return source if no tags are found
28
+ if (!source) return source
29
+ if (!strings.test.some(str => source.includes(str))) return source
30
+ // regexp for partials and hooks within liquid tags
31
+ const tags = /{%[-]?\s*((partial|hook|theme)\s['|"](\S*)['|"])\s*[-]?%}/gm
32
+ const inLiquid = /(?<!{%[-]?\s)((partial|hook)\s['|"](.*)['|"])/gm
33
+ const foundTags = [...source.matchAll(tags), ...source.matchAll(inLiquid)]
34
+ // gather dependencies, will be used to trigger rebuilds
35
+ const dependencies = listDependencies({ source, filePath })
36
+ ThemeEnvy.dependencies[filePath] = dependencies
37
+
38
+ foundTags.forEach(tag => {
39
+ source = replaceTag({ tag, source, filePath })
40
+ })
41
+ return source
42
+ }
43
+
44
+ function replaceTag({ tag, source, filePath } = {}) {
45
+ // we will replace tag[0] with the file contents
46
+ const replace = tag[0]
47
+ const action = tag[2]
48
+ const name = tag[3]
49
+ if (action === 'partial') {
50
+ const partialPath = globbedPartials.filter(partial => partial.includes(`/${`${name}.liquid`}`))[0]
51
+ const partialSource = fs.readFileSync(partialPath, 'utf8')
52
+ const file = extendLiquid({ source: partialSource, filePath: partialPath })
53
+ source = source.replace(replace, whitespace(replace, file))
54
+ }
55
+ if (action === 'hook') {
56
+ const replacedContent = ThemeEnvy.hooks[name] ? extendLiquid({ source: ThemeEnvy.hooks[name].content, filePath }) : ''
57
+ if (ThemeEnvy.hooks[name]) ThemeEnvy.hooks[name].replaced = true
58
+ source = source.replace(replace, replacedContent)
59
+ }
60
+ if (action === 'theme') {
61
+ // defines all properties available to {% theme name.key %} tags
62
+ const Theme = require(path.resolve(process.cwd(), 'theme.config.js'))
63
+ const [obj, key] = name.split('.')
64
+ source = source.replace(replace, Theme[obj][key])
65
+ }
66
+ return source
67
+ }
68
+
69
+ function whitespace(replace, source) {
70
+ const trimLeft = replace.includes('{%-')
71
+ const trimRight = replace.includes('-%}')
72
+ if (trimLeft) source = source.trimStart()
73
+ if (trimRight) source = source.trimEnd()
74
+ return source
75
+ }
76
+
77
+ module.exports = extendLiquid
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @private
3
+ * @file Helper function to determine correct output path for input file
4
+ * @param {string} path - path to file
5
+ * @returns {string} - output path
6
+ */
7
+
8
+ module.exports = function(path) {
9
+ const filename = path.slice(path.lastIndexOf('/') + 1)
10
+ if (path.includes('snippets/')) {
11
+ return `snippets/${filename}`
12
+ }
13
+ if (path.includes('sections/')) {
14
+ return `sections/${filename}`
15
+ }
16
+ if (path.includes('layout/')) {
17
+ return `layout/${filename}`
18
+ }
19
+ if (path.includes('templates/customers')) {
20
+ return `templates/customers/${filename}`
21
+ }
22
+ if (path.includes('templates/') && !path.includes('customers/')) {
23
+ return `templates/${filename}`
24
+ }
25
+ if (path.includes('assets/')) {
26
+ return `assets/${filename}`
27
+ }
28
+ return false
29
+ }
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ extendLiquidDependencies: require('./list-dependencies'),
3
+ extendLiquid: require('./extend-liquid'),
4
+ flattenShopifyDirectoryStructure: require('./flatten-shopify-directory-structure'),
5
+ sectionSchemaInject: require('./section-schema-inject'),
6
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @private
3
+ * @file Helper function that returns a list of files that reference the given liquid file
4
+ * @param {string} filePath - path to file to find dependencies for
5
+ * @param {string} source - source code of file to find dependencies for
6
+ * @returns {array} - list of files that reference the given file
7
+ */
8
+ const path = require('path')
9
+ const fs = require('fs-extra')
10
+ const getAll = require('#Build/functions/get-all.js')
11
+ // pre glob all liquid partials
12
+ const globbedPartials = getAll('partials')
13
+ const chalk = require('chalk')
14
+ const logSymbols = require('#LogSymbols')
15
+
16
+ const listDependencies = ({ filePath, source }) => {
17
+ const dependencies = []
18
+ const tags = /{%[-]?\s*((partial|hook|theme)\s['|"](\S*)['|"])\s*[-]?%}/gm
19
+ const inLiquid = /(?<!{%[-]?\s)((partial|hook)\s['|"](.*)['|"])/gm
20
+ const foundTags = [...source.matchAll(tags), ...source.matchAll(inLiquid)]
21
+ foundTags.forEach(tag => {
22
+ const action = tag[2]
23
+ const name = tag[3]
24
+ if (action === 'partial') {
25
+ const file = globbedPartials.filter(partial => partial.includes(`/${`${name}.liquid`}`))
26
+ // if partialPath doesn't return anything, exit process and output error
27
+ if (file.length === 0) {
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`)
29
+ process.exit()
30
+ }
31
+ if (file[0] === filePath) return
32
+ if (dependencies.indexOf(file[0]) === -1) dependencies.push(file[0])
33
+ }
34
+ if (action === 'hook') {
35
+ if (filePath && dependencies.indexOf(filePath) === -1) dependencies.push(filePath)
36
+ }
37
+ })
38
+ dependencies.forEach(file => {
39
+ if (file === filePath) return
40
+ const fileSource = fs.readFileSync(file, 'utf8')
41
+ dependencies.concat(listDependencies({ source: fileSource, filePath: file }))
42
+ })
43
+ const resolvedPaths = dependencies.map(file => path.resolve(file))
44
+ return resolvedPaths
45
+ }
46
+
47
+ module.exports = listDependencies
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @file Prebuild helper script that will inject the schema js file into the corresponding sections/*.liquid file
3
+ * @example
4
+ * // include the schema in the section liquid file
5
+ * {% schema 'schema-file.js' %}
6
+ */
7
+
8
+ module.exports = function({ source, filePath }) {
9
+ if (!filePath.includes('sections')) return source
10
+ const schema = source.match(/{% schema '(.*)' %}/g) || source.match(/{% schema "(.*)" %}/g)
11
+ // if there are no schema tags with a filename string, return
12
+ if (!schema) return source
13
+ // regexp for a quoted string within our schema match
14
+ const schemaFile = schema[0].match(/'(.*)'/)[1] || schema[0].match(/"(.*)"/)[1]
15
+ // load the file export
16
+ const schemaSource = ThemeRequire(schemaFile, { loader: filePath })
17
+ // replace the {% schema %} tag with the schema string and update asset
18
+ return source.replace(schema[0], formatSchema(schemaSource))
19
+ }
20
+
21
+ function formatSchema(schema) {
22
+ return `{% schema %}
23
+ ${JSON.stringify(schema, null, 2)}
24
+ {% endschema %}
25
+ `
26
+ }