@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,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Processes our liquid files during build
|
|
3
|
+
* @description replaces our custom hooks, partials, and theme tags, and injects section schema into section files
|
|
4
|
+
* @param {string} file - path to file
|
|
5
|
+
* @param {string} mode - 'development' or 'production'
|
|
6
|
+
* @returns {Void}
|
|
7
|
+
*/
|
|
8
|
+
const path = require('path')
|
|
9
|
+
const fs = require('fs-extra')
|
|
10
|
+
const { extendLiquid, flattenShopifyDirectoryStructure, sectionSchemaInject } = require('./functions')
|
|
11
|
+
const { liquidPrettify } = require('#Helpers')
|
|
12
|
+
|
|
13
|
+
module.exports = function({ file, mode, verbose }) {
|
|
14
|
+
const shopifyPath = flattenShopifyDirectoryStructure(file)
|
|
15
|
+
// skip files that don't need to be processed because they don't have an output path
|
|
16
|
+
if (!shopifyPath) return
|
|
17
|
+
|
|
18
|
+
const outputPath = `${ThemeEnvy.outputPath}/${shopifyPath}`
|
|
19
|
+
let source = fs.readFileSync(file, 'utf8')
|
|
20
|
+
|
|
21
|
+
// inject schema .js into liquid section files
|
|
22
|
+
if (file.includes('sections/')) source = sectionSchemaInject({ source, filePath: file })
|
|
23
|
+
|
|
24
|
+
// apply our custom liquid tags: partial, hook, theme
|
|
25
|
+
source = extendLiquid({ source, filePath: file })
|
|
26
|
+
|
|
27
|
+
// prettify our liquid if possible
|
|
28
|
+
if (mode === 'production') source = liquidPrettify({ source, pathname: outputPath, verbose })
|
|
29
|
+
|
|
30
|
+
// create output directory if it doesn't exist
|
|
31
|
+
fs.ensureDirSync(path.dirname(outputPath))
|
|
32
|
+
// save our file
|
|
33
|
+
fs.writeFileSync(outputPath, source)
|
|
34
|
+
// update progress bar
|
|
35
|
+
ThemeEnvy.progress.increment('liquid', 1)
|
|
36
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @private
|
|
3
|
+
* @file Manages ThemeEnvy build process
|
|
4
|
+
* @param {Object} opts
|
|
5
|
+
* @param {Boolean} opts.watch
|
|
6
|
+
* @param {Boolean} opts.verbose
|
|
7
|
+
* @returns {Promise}
|
|
8
|
+
*/
|
|
9
|
+
const fs = require('fs-extra')
|
|
10
|
+
const path = require('path')
|
|
11
|
+
const liquid = require('./liquid')
|
|
12
|
+
const getAll = require('./get-all')
|
|
13
|
+
const failedHookInstalls = require('./failed-hook-installs')
|
|
14
|
+
|
|
15
|
+
module.exports = function({ mode, opts }) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const verbose = opts.verbose || false
|
|
18
|
+
// build files
|
|
19
|
+
build({ mode, verbose })
|
|
20
|
+
.then(() => {
|
|
21
|
+
// watch for changes
|
|
22
|
+
if (opts.watch) {
|
|
23
|
+
ThemeEnvy.events.on('build:complete', () => {
|
|
24
|
+
watch({ mode, verbose })
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
resolve()
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function build({ mode, files = [], verbose }) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
if (files.length > 0) {
|
|
35
|
+
// remove partials and schema = require(files list)
|
|
36
|
+
files = files.filter((file) => !file.includes('partials/') && !file.includes('schema/'))
|
|
37
|
+
}
|
|
38
|
+
/*
|
|
39
|
+
if we have files passed in (during watch process), use those
|
|
40
|
+
otherwise glob for all liquid files
|
|
41
|
+
*/
|
|
42
|
+
const liquidFiles = files.length > 0
|
|
43
|
+
? files.filter(file => file.includes('.liquid'))
|
|
44
|
+
: getAll('liquid')
|
|
45
|
+
|
|
46
|
+
const sectionGroups = files.length > 0
|
|
47
|
+
? files.filter(file => file.includes('.json'))
|
|
48
|
+
: getAll('sectionGroups')
|
|
49
|
+
|
|
50
|
+
// process all liquid files and output to dist directory
|
|
51
|
+
if (liquidFiles.length > 0) {
|
|
52
|
+
liquidFiles.forEach((file) => {
|
|
53
|
+
liquid({ file, mode, verbose })
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
// check for install hooks that reference non-existent hooks
|
|
57
|
+
failedHookInstalls()
|
|
58
|
+
ThemeEnvy.progress.increment('failedHookInstalls')
|
|
59
|
+
|
|
60
|
+
// copy sectionGroup files to dist
|
|
61
|
+
if (sectionGroups.length > 0) {
|
|
62
|
+
sectionGroups.forEach((file) => {
|
|
63
|
+
fs.copyFileSync(file, path.resolve(ThemeEnvy.outputPath, 'sections', path.basename(file)))
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
ThemeEnvy.progress.increment('sectionGroups')
|
|
67
|
+
resolve()
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function watch({ mode, verbose }) {
|
|
72
|
+
const chokidar = require('chokidar')
|
|
73
|
+
console.log('watching for changes...')
|
|
74
|
+
chokidar.watch(ThemeEnvy.themePath).on('change', (path) => {
|
|
75
|
+
ThemeEnvy.events.emit('watch:start')
|
|
76
|
+
const isJSONTemplate = path.includes('templates/') && path.extname(path) === '.json'
|
|
77
|
+
if (!isJSONTemplate) {
|
|
78
|
+
console.log(`updated: ${path.split(ThemeEnvy.themePath + '/')[1]}`)
|
|
79
|
+
build({ files: [path], mode })
|
|
80
|
+
.then(() => {
|
|
81
|
+
// rebuild complete
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @private
|
|
3
|
+
* @file Runs webpack during ThemeEnvy build
|
|
4
|
+
* @param {Object} options - options object
|
|
5
|
+
* @param {string} options.mode - webpack mode, either 'development' or 'production'
|
|
6
|
+
* @param {Object} options.opts - options object
|
|
7
|
+
* @param {Boolean} options.opts.watch - whether or not to run webpack in watch mode
|
|
8
|
+
* @returns {Promise}
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const webpack = require('webpack')
|
|
12
|
+
const getAll = require('./get-all')
|
|
13
|
+
|
|
14
|
+
module.exports = function({ mode, opts }) {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const watch = opts.watch || false
|
|
17
|
+
|
|
18
|
+
const webpackConfig = require('#Build/theme-envy.config.js')
|
|
19
|
+
// run webpack
|
|
20
|
+
// set our webpack mode
|
|
21
|
+
webpackConfig.mode = mode
|
|
22
|
+
// set our webpack optimization
|
|
23
|
+
webpackConfig.optimization.minimize = mode === 'production'
|
|
24
|
+
// set our webpack watch flag
|
|
25
|
+
webpackConfig.watch = watch
|
|
26
|
+
// merge our theme config named entries into webackConfig.entry
|
|
27
|
+
webpackConfig.entry = { ...webpackConfig.entry, ...ThemeEnvy.entry }
|
|
28
|
+
|
|
29
|
+
if (ThemeEnvy?.tailwind === false && getAll('criticalCSS').length > 0) {
|
|
30
|
+
// 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`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
webpack(webpackConfig, (err, stats) => {
|
|
35
|
+
if (err || stats.hasErrors()) {
|
|
36
|
+
console.log(stats, err)
|
|
37
|
+
reject(err)
|
|
38
|
+
}
|
|
39
|
+
// Done processing - finish up progress bar and make up for our initial increment
|
|
40
|
+
ThemeEnvy.progress.increment('webpack')
|
|
41
|
+
resolve(stats)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Processes all liquid files in the themePath directory and output them to the outputPath directory.
|
|
3
|
+
* @param {string} env - The mode to build in, either 'development' or 'production'
|
|
4
|
+
* @param {Object} opts - The options object
|
|
5
|
+
* @param {boolean} opts.verbose - Whether or not to log warnings
|
|
6
|
+
* @param {boolean} opts.watch - Whether or not to watch for changes
|
|
7
|
+
* @example
|
|
8
|
+
* // build in production mode
|
|
9
|
+
* build('production')
|
|
10
|
+
* @example
|
|
11
|
+
* // run from the CLI
|
|
12
|
+
* npx theme-envy build [production|development] [--watch|w] [--verbose|v]
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const path = require('path')
|
|
16
|
+
const chalk = require('chalk')
|
|
17
|
+
const emoji = require('node-emoji')
|
|
18
|
+
const { themeEnvy, webpack, tailwind } = require('#Build/functions')
|
|
19
|
+
const { distClean } = require('#Helpers')
|
|
20
|
+
|
|
21
|
+
module.exports = async function(env, opts = {}) {
|
|
22
|
+
const mode = env || 'production'
|
|
23
|
+
|
|
24
|
+
// empty dist folder for a clean build, do not log the clean message
|
|
25
|
+
distClean({ quiet: true })
|
|
26
|
+
|
|
27
|
+
// our pretty build message
|
|
28
|
+
const relativeDistPath = path.relative(process.cwd(), ThemeEnvy.outputPath)
|
|
29
|
+
console.log(
|
|
30
|
+
emoji.get('hammer'),
|
|
31
|
+
chalk.cyan(`Building ./${relativeDistPath} in`),
|
|
32
|
+
mode === 'development' ? chalk.yellow.bold(mode) : chalk.magenta.bold(mode),
|
|
33
|
+
chalk.cyan('mode')
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
require('./requires')
|
|
37
|
+
|
|
38
|
+
await themeEnvy({ mode, opts })
|
|
39
|
+
|
|
40
|
+
if (ThemeEnvy?.tailwind !== false) await tailwind({ mode, opts })
|
|
41
|
+
|
|
42
|
+
await webpack({ mode, opts })
|
|
43
|
+
|
|
44
|
+
ThemeEnvy.events.emit('build:complete')
|
|
45
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Copies all locales files from the src folder to the dist folder with no transformations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs-extra')
|
|
6
|
+
const path = require('path')
|
|
7
|
+
const glob = require('glob')
|
|
8
|
+
|
|
9
|
+
const assets = glob.sync(path.resolve(ThemeEnvy.themePath, '**/assets/**/*.*'))
|
|
10
|
+
|
|
11
|
+
// if dist/assets doesn't exist create it
|
|
12
|
+
fs.ensureDirSync(path.resolve(ThemeEnvy.outputPath, 'assets'))
|
|
13
|
+
|
|
14
|
+
assets.forEach(asset => {
|
|
15
|
+
try {
|
|
16
|
+
fs.copySync(asset, path.resolve(ThemeEnvy.outputPath, 'assets', path.basename(asset)))
|
|
17
|
+
// update progress bar
|
|
18
|
+
ThemeEnvy.progress.increment('assets', 1)
|
|
19
|
+
} catch (err) {
|
|
20
|
+
console.error(err)
|
|
21
|
+
}
|
|
22
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file collects and concatenates all config/*.js files into a single settings_schema.json file
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const fs = require('fs-extra')
|
|
7
|
+
const { getAll } = require('#Build/functions')
|
|
8
|
+
const requiredModules = []
|
|
9
|
+
// glob all our config/*.js files
|
|
10
|
+
const writeSettingsSchema = () => {
|
|
11
|
+
const configInputPath = path.resolve(ThemeEnvy.themePath, 'config')
|
|
12
|
+
const configOutputPath = path.resolve(ThemeEnvy.outputPath, 'config')
|
|
13
|
+
|
|
14
|
+
const settingsSchemaPath = path.resolve(configInputPath, 'settings_schema.js')
|
|
15
|
+
const settingsSchema = require(settingsSchemaPath)
|
|
16
|
+
if (requiredModules.indexOf(settingsSchemaPath) === -1) requiredModules.push(settingsSchemaPath)
|
|
17
|
+
const globbedConfigs = getAll('config')
|
|
18
|
+
.map(file => {
|
|
19
|
+
if (requiredModules.indexOf(file) === -1) requiredModules.push(file)
|
|
20
|
+
return require(file)
|
|
21
|
+
})
|
|
22
|
+
.flat()
|
|
23
|
+
// sort alphabetically by name
|
|
24
|
+
.sort((a, b) => (a.name > b.name) ? 1 : -1)
|
|
25
|
+
|
|
26
|
+
const config = [...settingsSchema, ...globbedConfigs].flat()
|
|
27
|
+
|
|
28
|
+
fs.ensureDirSync(configOutputPath)
|
|
29
|
+
fs.writeFileSync(path.resolve(configOutputPath, 'settings_schema.json'), JSON.stringify(config, null, 2))
|
|
30
|
+
fs.copyFileSync(path.resolve(configInputPath, 'settings_data.json'), path.resolve(configOutputPath, 'settings_data.json'))
|
|
31
|
+
// update progress bar
|
|
32
|
+
ThemeEnvy.progress.increment('config')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
writeSettingsSchema()
|
|
36
|
+
|
|
37
|
+
ThemeEnvy.events.on('watch:start', () => {
|
|
38
|
+
// clear node cache of ThemeRequired modules
|
|
39
|
+
requiredModules.forEach(module => {
|
|
40
|
+
delete require.cache[require.resolve(module)]
|
|
41
|
+
})
|
|
42
|
+
requiredModules.length = 0
|
|
43
|
+
writeSettingsSchema()
|
|
44
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
})()
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @private
|
|
3
|
+
* @file Collect all the install.js files that insert code into the theme hooks
|
|
4
|
+
* @description
|
|
5
|
+
* Find all files in the src directory that end with install.js
|
|
6
|
+
* These files should export an array of objects with the following properties:
|
|
7
|
+
* hook: the name of the hook to insert the code into
|
|
8
|
+
* content: the code to insert
|
|
9
|
+
* priority: the priority of the code, 0-100, default 50
|
|
10
|
+
*
|
|
11
|
+
module.exports = [
|
|
12
|
+
{
|
|
13
|
+
hook: 'head-start',
|
|
14
|
+
content: '{% partial "_test-partial" %}',
|
|
15
|
+
priority: 50
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
*/
|
|
19
|
+
const { getAll } = require('#Build/functions')
|
|
20
|
+
const path = require('path');
|
|
21
|
+
(() => {
|
|
22
|
+
const installs = getAll('installs')
|
|
23
|
+
// collect all installs
|
|
24
|
+
ThemeEnvy.installs = installs.map(file => require(path.resolve(file))).flat()
|
|
25
|
+
|
|
26
|
+
const hooks = ThemeEnvy.installs.filter(entry => entry.hook)
|
|
27
|
+
const schema = ThemeEnvy.installs.filter(entry => !entry.hook)
|
|
28
|
+
|
|
29
|
+
ThemeEnvy.hooks = {}
|
|
30
|
+
ThemeEnvy.schema = {}
|
|
31
|
+
// group all hooks together by target file and hook
|
|
32
|
+
// This is so we can install hooks once per destination file...
|
|
33
|
+
hooks.forEach(entry => {
|
|
34
|
+
ThemeEnvy.hooks[entry.hook] = ThemeEnvy.hooks[entry.hook] || []
|
|
35
|
+
// add the hook to the destination, process entry.content for softlimit partials
|
|
36
|
+
// priority 0-100, default 50
|
|
37
|
+
ThemeEnvy.hooks[entry.hook].push({ content: entry.content, priority: entry.priority || 50 })
|
|
38
|
+
})
|
|
39
|
+
// sort hook content by priority and concat into a single string
|
|
40
|
+
Object.keys(ThemeEnvy.hooks).forEach(hook => {
|
|
41
|
+
ThemeEnvy.hooks[hook] = {
|
|
42
|
+
content: ThemeEnvy.hooks[hook].sort((a, b) => a.priority - b.priority).map(entry => entry.content).join('\n')
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
// TODO: LOOK INTO THIS
|
|
47
|
+
schema.forEach(entry => {
|
|
48
|
+
const file = `${entry.file}.liquid`
|
|
49
|
+
ThemeEnvy.schema[file] = ThemeEnvy.schema[file] || {
|
|
50
|
+
settings: [],
|
|
51
|
+
blocks: [],
|
|
52
|
+
}
|
|
53
|
+
// add the schema to the destination, process entry.content for softlimit partials
|
|
54
|
+
// target is either 'settings' or 'blocks'
|
|
55
|
+
const target = entry.target.split('.')[1]
|
|
56
|
+
ThemeEnvy.schema[file][target].push(entry.content)
|
|
57
|
+
})
|
|
58
|
+
})()
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @private
|
|
3
|
+
* @file Manages our progress bar during the build process
|
|
4
|
+
*/
|
|
5
|
+
const { getAll } = require('#Build/functions')
|
|
6
|
+
const cliProgress = require('cli-progress')
|
|
7
|
+
const colors = require('ansi-colors');
|
|
8
|
+
|
|
9
|
+
(() => {
|
|
10
|
+
// setup all of our progress bar steps
|
|
11
|
+
// these are then used to increment the progress bar
|
|
12
|
+
// ThemeEnvy.progress.increment('label', step)
|
|
13
|
+
const counts = {
|
|
14
|
+
assets: getAll('assets').length,
|
|
15
|
+
config: 1,
|
|
16
|
+
failedHookInstalls: 1,
|
|
17
|
+
globals: 1,
|
|
18
|
+
liquid: getAll('liquid').length,
|
|
19
|
+
locales: 1,
|
|
20
|
+
requires: 7,
|
|
21
|
+
sectionGroups: 1,
|
|
22
|
+
tailwind: ThemeEnvy?.tailwind !== false ? 1 : 0,
|
|
23
|
+
templates: getAll('templates').length,
|
|
24
|
+
webpack: 1,
|
|
25
|
+
}
|
|
26
|
+
const bar = new cliProgress.SingleBar({
|
|
27
|
+
format: colors.cyan('{bar}') + ' {percentage}% | {duration_formatted}',
|
|
28
|
+
barCompleteChar: '\u2588',
|
|
29
|
+
barIncompleteChar: '\u2591',
|
|
30
|
+
hideCursor: true,
|
|
31
|
+
}, cliProgress.Presets.shades_classic)
|
|
32
|
+
|
|
33
|
+
ThemeEnvy.progress = {
|
|
34
|
+
bar,
|
|
35
|
+
increment(label, step) {
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} label - the label of the progress bar to increment
|
|
38
|
+
* @param {number} step - the number of steps to increment the progress bar by if different than the entry in counts
|
|
39
|
+
*/
|
|
40
|
+
step = step || (counts[label] ? counts[label] : 1)
|
|
41
|
+
bar.increment(step)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const totalBuildSteps = Object.values(counts).reduce((a, b) => a + b, 0)
|
|
46
|
+
|
|
47
|
+
bar.start(totalBuildSteps, 0)
|
|
48
|
+
|
|
49
|
+
ThemeEnvy.events.on('build:complete', () => {
|
|
50
|
+
bar.stop()
|
|
51
|
+
})
|
|
52
|
+
})()
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file defines our global ThemeRequire function used for loading schema files
|
|
3
|
+
@param {string} file - the filename to load
|
|
4
|
+
@param {object} options - options to pass to the ThemeRequire function
|
|
5
|
+
@param {array} options.delete - an array of schema keys to delete
|
|
6
|
+
@param {object} options.extend - an object of schema keys to extend
|
|
7
|
+
@param {number} options.loop - a number of times to loop the schema
|
|
8
|
+
@param {object} options.suffix
|
|
9
|
+
@returns {object} - the schema object
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const glob = require('glob')
|
|
13
|
+
const path = require('path')
|
|
14
|
+
const requiredModules = []
|
|
15
|
+
const { getAll } = require('#Build/functions')
|
|
16
|
+
const chalk = require('chalk')
|
|
17
|
+
const logSymbols = require('#LogSymbols')
|
|
18
|
+
|
|
19
|
+
let allSchema
|
|
20
|
+
|
|
21
|
+
const setPreGlobs = () => {
|
|
22
|
+
allSchema = getAll('schema')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// uncache all required files after a build is complete so changes can be made to partials and schemas in between watch events
|
|
26
|
+
ThemeEnvy.events.on('watch:start', () => {
|
|
27
|
+
// clear node cache of ThemeRequired modules
|
|
28
|
+
requiredModules.forEach(module => {
|
|
29
|
+
delete require.cache[require.resolve(module)]
|
|
30
|
+
})
|
|
31
|
+
requiredModules.length = 0
|
|
32
|
+
setPreGlobs()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const ThemeRequire = (file, options) => {
|
|
36
|
+
// check for file in our pre-globbed arrays
|
|
37
|
+
const ref = getFile(file)
|
|
38
|
+
|
|
39
|
+
if (ref.length === 0) {
|
|
40
|
+
console.error(logSymbols.error, chalk.red('Error:'), `Could not find file ${file}`)
|
|
41
|
+
process.exit()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const filePath = path.resolve(process.cwd(), ref[0])
|
|
45
|
+
let content
|
|
46
|
+
try {
|
|
47
|
+
content = require(filePath)
|
|
48
|
+
if (requiredModules.indexOf(filePath) === -1) requiredModules.push(filePath)
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('\n', logSymbols.error, chalk.red('Error:'), `Invalid JSON, ${error}\n`, chalk.dim(error.stack.split(error)[0]))
|
|
51
|
+
process.exit()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Add schema extend/delete support
|
|
55
|
+
/*
|
|
56
|
+
* ***********************
|
|
57
|
+
* Example usage:
|
|
58
|
+
* ***********************
|
|
59
|
+
const textLockupBlock = ThemeRequire(
|
|
60
|
+
'schema/schema-text-lockup-block.json',
|
|
61
|
+
{
|
|
62
|
+
delete: ['Text Background', 'text_bg_opacity'],
|
|
63
|
+
extend: {
|
|
64
|
+
title: {
|
|
65
|
+
default: 'Hello World'
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
loop: 3,
|
|
69
|
+
suffix: 2
|
|
70
|
+
})
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
// if module.export is a function, call it with options.args
|
|
74
|
+
if (typeof content === 'function') {
|
|
75
|
+
content = content(options?.args)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (options?.extend || options?.delete || options?.loop || options?.suffix || options?.args) {
|
|
79
|
+
// make copy of the schema to modify so we don't transform the original
|
|
80
|
+
content = parseJson(content)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (options?.delete) {
|
|
84
|
+
content = schemaDelete(content, options.delete)
|
|
85
|
+
}
|
|
86
|
+
if (options?.extend) {
|
|
87
|
+
content = schemaExtend(content, options.extend)
|
|
88
|
+
}
|
|
89
|
+
if (options?.loop) {
|
|
90
|
+
content = schemaLoop(file, content, options.loop)
|
|
91
|
+
}
|
|
92
|
+
if (options?.suffix) {
|
|
93
|
+
content = schemaSuffix(file, content, options.suffix)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (options?.loader) {
|
|
97
|
+
ThemeEnvy.dependencies[options.loader] = ThemeEnvy.dependencies[options.loader] || []
|
|
98
|
+
ThemeEnvy.dependencies[options.loader].push(filePath)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return content
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function schemaDelete(file, src, del) {
|
|
105
|
+
const replaceWith = src.filter(entry => {
|
|
106
|
+
if (entry.settings) {
|
|
107
|
+
entry.settings = schemaDelete(entry.settings, del)
|
|
108
|
+
return entry
|
|
109
|
+
}
|
|
110
|
+
return ((entry.id ? !del.includes(entry.id) : false) || (entry.content ? !del.includes(entry.content) : false))
|
|
111
|
+
})
|
|
112
|
+
return replaceWith
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function schemaExtend(src, exts) {
|
|
116
|
+
let replaceWith
|
|
117
|
+
Object.entries(exts).forEach(ext => {
|
|
118
|
+
// map extension to schema
|
|
119
|
+
replaceWith = src.map(entry => {
|
|
120
|
+
if (entry.settings) {
|
|
121
|
+
schemaExtend(entry.settings, exts)
|
|
122
|
+
}
|
|
123
|
+
if (entry.id !== ext[0] && entry.content !== ext[0]) return entry
|
|
124
|
+
Object.entries(ext[1]).forEach(val => {
|
|
125
|
+
entry[val[0]] = val[1]
|
|
126
|
+
})
|
|
127
|
+
return entry
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
return replaceWith
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function schemaLoop(file, src, loop) {
|
|
134
|
+
const replaceWith = []
|
|
135
|
+
for (let i = 1; i < (loop + 1); i++) {
|
|
136
|
+
const srcCopy = parseJson(src)
|
|
137
|
+
srcCopy.map(entry => {
|
|
138
|
+
if (entry.id) entry.id = `${entry.id}_${i}`
|
|
139
|
+
if (entry.label) entry.label = `${entry.label} ${i}`
|
|
140
|
+
if (entry.type === 'header') entry.content = `${entry.content} ${i}`
|
|
141
|
+
return entry
|
|
142
|
+
})
|
|
143
|
+
replaceWith.push(srcCopy)
|
|
144
|
+
}
|
|
145
|
+
return replaceWith
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function schemaSuffix(file, src, suffix) {
|
|
149
|
+
const replaceWith = parseJson(src)
|
|
150
|
+
replaceWith.map(entry => {
|
|
151
|
+
if (entry.id) entry.id = `${entry.id}_${suffix}`
|
|
152
|
+
if (entry.label) entry.label = `${entry.label} ${suffix}`
|
|
153
|
+
if (entry.type === 'header') entry.content = `${entry.content} ${suffix}`
|
|
154
|
+
return entry
|
|
155
|
+
})
|
|
156
|
+
return replaceWith
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function getSchemaFile(file) {
|
|
160
|
+
return allSchema.filter(schema => path.basename(schema) === path.basename(file))
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function getFile(file) {
|
|
164
|
+
if (file.includes('schema')) {
|
|
165
|
+
return getSchemaFile(file)
|
|
166
|
+
} else {
|
|
167
|
+
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
|
+
}
|
|
171
|
+
return res
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* parse JSON for schema extend functions, return error if JSON not readable
|
|
176
|
+
@param src [object] - the file contents to be extended
|
|
177
|
+
@param schemaRef [string] - source schema file name being extended
|
|
178
|
+
*/
|
|
179
|
+
function parseJson(src, schemaRef) {
|
|
180
|
+
try {
|
|
181
|
+
return JSON.parse(JSON.stringify(src))
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.error('\n', logSymbols.error, chalk.red('Error:'), `Invalid JSON, ${error}\n`, chalk.dim(error.stack.split(error)[0]))
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// run setPreGlobs on initial load
|
|
188
|
+
setPreGlobs()
|
|
189
|
+
// set global access to ThemeRequire
|
|
190
|
+
global.ThemeRequire = ThemeRequire
|