@softlimit/theme-envy 0.1.2-alpha
Sign up to get free protection for your applications and to get access to all the features.
- 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
|