@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
@@ -0,0 +1,12 @@
1
+ const getDepth = (obj, depth = 0, arr = []) => {
2
+ // collect object depth and key for each entry into an array so we can output a smaller, less verbose version of the tree
3
+ Object.keys(obj).forEach(key => {
4
+ arr.push({ depth, key, type: obj[key].type })
5
+ if (obj[key].tree) {
6
+ getDepth(obj[key].tree, depth + 1, arr)
7
+ }
8
+ })
9
+ return arr
10
+ }
11
+
12
+ module.exports = getDepth
@@ -0,0 +1,11 @@
1
+ const path = require('path')
2
+
3
+ module.exports = (filePath) => {
4
+ const file = {
5
+ name: path.basename(filePath, '.liquid'),
6
+ type: filePath.includes('sections/') ? 'section' : filePath.includes('snippets/') ? 'snippet' : filePath.includes('partials/') ? 'partial' : filePath.includes('layout/') ? 'layout' : filePath.includes('templates/') ? 'template' : filePath.includes('assets/') ? 'asset' : null,
7
+ path: path.resolve(filePath),
8
+ tree: {}
9
+ }
10
+ return file
11
+ }
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ countResults: require('./count-results.js'),
3
+ getDepth: require('./get-depth.js'),
4
+ getFileInfo: require('./get-file-info.js'),
5
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ @file returns an array of all liquid files that are in the dependency tree of the given file.
3
+ @example npx theme-envy tree filepath1,filepath2,filepath3
4
+ */
5
+
6
+ const chalk = require('chalk')
7
+ const { dependencies } = require('./objects')
8
+ const { countResults, getDepth } = require('./functions')
9
+
10
+ module.exports = function(filepaths, options) {
11
+ const files = filepaths.split(',')
12
+ if (!files) {
13
+ console.error('Provide relative file path(s) to find')
14
+ return
15
+ }
16
+ // create our output and run our functions on each file passed in as a command line argument
17
+ files.forEach((filePath, i) => {
18
+ // add a new line between each file
19
+ if (i > 0) console.log('\n')
20
+ const result = dependencies.check(filePath)
21
+ console.log(chalk.bgWhite.black.bold('------------------------------------------'))
22
+ console.log(chalk.bgWhite.black.bold(` ${countResults(result)} files in liquid dependency tree for: \n ${filePath} `))
23
+ console.log(chalk.bgWhite.black.bold('------------------------------------------'))
24
+ if (options.verbose) {
25
+ // our "verbose" output
26
+ console.dir(result, { depth: null, colors: true })
27
+ } else {
28
+ // our "simple" output
29
+ getDepth(result).forEach(entry => {
30
+ let weight, bg
31
+ if (entry.type === 'section') {
32
+ bg = 'bgGray'
33
+ weight = 'bold'
34
+ } else {
35
+ bg = 'bgBlack'
36
+ weight = entry.depth === 0 ? 'bold' : false
37
+ }
38
+ const color = entry.depth === 0 ? 'green' : entry.depth === 1 ? 'yellow' : entry.depth === 2 ? 'blue' : entry.depth === 3 ? 'red' : 'gray'
39
+ if (weight) {
40
+ // output our bold items
41
+ console.log(chalk[weight][color](`${' '.repeat(entry.depth)}${chalk[weight][color][bg](`${entry.key} (${entry.type})`)}`))
42
+ } else {
43
+ console.log(chalk[color](`${' '.repeat(entry.depth)}${entry.key} (${entry.type})`))
44
+ }
45
+ })
46
+ }
47
+ })
48
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * @file This file contains the logic for collecting all of the dependencies for a given file.
3
+ */
4
+
5
+ const fs = require('fs')
6
+ const glob = require('glob')
7
+ const path = require('path')
8
+ const { getFileInfo } = require('../functions')
9
+
10
+ // Collect all of our liquid files
11
+ const liquidFiles = glob.sync(path.resolve(ThemeEnvy.themePath, '**/*.liquid'))
12
+ // define our strings that we want to find in the files
13
+ const searchStrings = (file) => {
14
+ return {
15
+ asset: [`'${file.name}' | asset_url`, `"${file.name}" | asset_url`],
16
+ snippet: [`render '${file.name}'`, `render "${file.name}"`, `include '${file.name}'`, `include "${file.name}"`],
17
+ partial: [`partial '${file.name}'`, `partial "${file.name}"`],
18
+ section: [`section '${file.name}'`, `section "${file.name}"`],
19
+ }
20
+ }
21
+
22
+ module.exports = {
23
+ check(filePath) {
24
+ this.results = {}
25
+ const fileName = filePath.includes('.') ? filePath : path.basename(filePath, '.liquid')
26
+ this.results[fileName] = getFileInfo(filePath)
27
+ this.results = this.collect(this.results)
28
+
29
+ // remove all searchString attributes and empty trees from our results
30
+ this.results = this.cleanForReport(this.results)
31
+ return this.results
32
+ },
33
+ collect(results) {
34
+ // iterate through each file in our results object
35
+ Object.entries(results).forEach(file => {
36
+ const fileInfo = file[1]
37
+ // get the tree for the file
38
+ fileInfo.tree = this.getFileTree(fileInfo)
39
+ })
40
+ return results
41
+ },
42
+ getFileTree(fileInfo) {
43
+ // do not check "templates", or "layouts" for dependencies
44
+ if (['template', 'layout'].includes(fileInfo.type)) return fileInfo.tree
45
+ const tree = {}
46
+ this.numberOfFiles = this.numberOfFiles > 0 ? this.numberOfFiles : 0
47
+ // check all liquid files for references to the file
48
+ liquidFiles.filter(file => file !== fileInfo.path).forEach(liquid => {
49
+ const source = fs.readFileSync(path.resolve(process.cwd(), liquid), 'utf8')
50
+ searchStrings(fileInfo)[fileInfo.type].forEach(string => {
51
+ if (source.includes(string)) {
52
+ // add the partial to our results object and continue down its tree
53
+ const fileName = path.basename(liquid, '.liquid')
54
+ tree[fileName] = getFileInfo(liquid)
55
+ tree[fileName].tree = this.getFileTree(tree[fileName])
56
+ }
57
+ })
58
+ })
59
+ return tree
60
+ },
61
+ cleanForReport(results) {
62
+ // remove any empty trees from our results for a cleaner report
63
+ Object.entries(results).forEach(file => {
64
+ const fileInfo = file[1]
65
+ delete fileInfo.name
66
+ if (Object.keys(fileInfo.tree).length === 0) delete fileInfo.tree
67
+ })
68
+ // clean the sub trees
69
+ Object.entries(results).forEach(file => {
70
+ if (file[1].tree) file[1].tree = this.cleanForReport(file[1].tree)
71
+ })
72
+ return results
73
+ }
74
+ }
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ dependencies: require('./dependencies.js'),
3
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @file Returns the appropriate log symbols based on unicode support
3
+ * @example
4
+ * console.log(logSymbols.info, 'Info message')
5
+ * @returns {Object} - An object containing the appropriate log symbols
6
+ */
7
+ // based on https://github.com/sindresorhus/log-symbols
8
+
9
+ const chalk = require('chalk')
10
+ const isUnicodeSupported = require('./unicode-supported')
11
+
12
+ const main = {
13
+ info: chalk.cyan('ℹ'),
14
+ success: chalk.green('✔'),
15
+ warning: chalk.yellow('⚠'),
16
+ error: chalk.red('✖'),
17
+ }
18
+
19
+ const fallback = {
20
+ info: chalk.cyan('i'),
21
+ success: chalk.green('√'),
22
+ warning: chalk.yellow('‼'),
23
+ error: chalk.red('×'),
24
+ }
25
+
26
+ const logSymbols = isUnicodeSupported() ? main : fallback
27
+
28
+ module.exports = logSymbols
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @file A function that pulls the JSON files from Shopify and copies them to the theme directory.
3
+ * @example
4
+ * npx theme-envy pull-json
5
+ * @returns {Void}
6
+ */
7
+
8
+ const glob = require('glob')
9
+ const path = require('path')
10
+ const fs = require('fs-extra')
11
+ const { spawn } = require('child_process')
12
+
13
+ module.exports = function() {
14
+ const relativeDistPath = path.relative(process.cwd(), ThemeEnvy.outputPath)
15
+ const themePull = ['theme', 'pull', `--store=${ThemeEnvy.store}`, `--path=${relativeDistPath}`, '--only=templates/*.json,config/settings_data.json,sections/*.json']
16
+ const shopify = spawn('shopify', themePull, { cwd: ThemeEnvy.outputPath, stdio: 'inherit' })
17
+
18
+ shopify.on('exit', function() {
19
+ const files = glob.sync(path.resolve(ThemeEnvy.outputPath, '{templates,config,sections}/**/*.json')).filter(file => file.indexOf('settings_schema') > -1)
20
+ files.forEach(file => fs.copyFileSync(file, file.replace(ThemeEnvy.outputPath, ThemeEnvy.themePath)))
21
+ })
22
+ }
@@ -0,0 +1,15 @@
1
+ const path = require('path')
2
+ const fs = require('fs-extra')
3
+ const upperFirstLetter = require('./upper-first-letter')
4
+ const starterContent = require('./starter-content')
5
+ const { starterConfigs } = require('../objects')
6
+
7
+ module.exports = (name) => {
8
+ const ELEMENTS = path.resolve(ThemeEnvy.themePath, 'theme-envy/elements')
9
+ const EXT_NAME = name.toLowerCase()
10
+
11
+ const EXT_COMPONENT_NAME = EXT_NAME.indexOf('-') > -1 ? EXT_NAME : EXT_NAME + '-component'
12
+ const EXT_CLASS_NAME = EXT_COMPONENT_NAME.split('-').map(part => upperFirstLetter(part)).join('')
13
+
14
+ fs.writeFileSync(path.resolve(ELEMENTS, `${EXT_NAME}.js`), starterContent(starterConfigs.element, [EXT_NAME, EXT_CLASS_NAME]))
15
+ }
@@ -0,0 +1,76 @@
1
+ const path = require('path')
2
+ const loadDir = require('./load-dir')
3
+ const upperFirstLetter = require('./upper-first-letter')
4
+ const starterContent = require('./starter-content')
5
+ const { starterConfigs } = require('../objects')
6
+
7
+ module.exports = (name, include) => {
8
+ const FEATURES = path.resolve(ThemeEnvy.themePath, 'theme-envy/features')
9
+ const starters = include?.split(',') || 'all'
10
+
11
+ const DIRS = []
12
+ const EXT_NAME = name.toLowerCase()
13
+ const INCLUDE_ANY = starters
14
+ const INCLUDE_ALL = INCLUDE_ANY && starters.includes('all')
15
+ const INCLUDE_ELEMENT = INCLUDE_ANY && (starters.includes('elements') || INCLUDE_ALL)
16
+ const INCLUDE_STYLE = INCLUDE_ANY && (starters.includes('styles') || INCLUDE_ALL)
17
+ const INCLUDE_INSTALL = INCLUDE_ANY && (starters.includes('install') || INCLUDE_ALL)
18
+ const INCLUDE_SECTION = INCLUDE_ANY && (starters.includes('sections') || INCLUDE_ALL)
19
+ const INCLUDE_SNIPPET = INCLUDE_ANY && (starters.includes('snippets') || INCLUDE_ALL)
20
+ const INCLUDE_CONFIG = INCLUDE_ANY && (starters.includes('config') || INCLUDE_ALL)
21
+ const INCLUDE_SCHEMA = INCLUDE_ANY && (starters.includes('schema') || INCLUDE_ALL)
22
+ const EXT_COMPONENT_NAME = EXT_NAME.indexOf('-') > -1 ? EXT_NAME : EXT_NAME + '-component'
23
+ const EXT_CLASS_NAME = EXT_COMPONENT_NAME.split('-').map(part => upperFirstLetter(part)).join('')
24
+ const EXT_READABLE_NAME = EXT_COMPONENT_NAME.split('-').map(part => upperFirstLetter(part)).join(' ')
25
+
26
+ // import strings
27
+ const scriptImport = ''
28
+ let styleImport = ''
29
+
30
+ /*
31
+ DEFAULT FILE CONTENTS & import strings
32
+ */
33
+
34
+ const FILES = {}
35
+ if (INCLUDE_CONFIG) {
36
+ DIRS.push('config')
37
+
38
+ FILES[`config/${EXT_NAME}.js`] = starterContent(starterConfigs.config, [EXT_READABLE_NAME])
39
+ }
40
+
41
+ if (INCLUDE_SCHEMA) {
42
+ DIRS.push('schema')
43
+ FILES[`schema/schema-${EXT_NAME}.js`] = starterContent(starterConfigs.schema, [EXT_NAME, EXT_READABLE_NAME])
44
+ }
45
+
46
+ if (INCLUDE_INSTALL) {
47
+ FILES['install.js'] = starterContent(starterConfigs.install, [EXT_NAME])
48
+ }
49
+ if (INCLUDE_ELEMENT) {
50
+ DIRS.push('elements')
51
+ FILES[`elements/${EXT_COMPONENT_NAME}.js`] = starterContent(starterConfigs.element, [EXT_COMPONENT_NAME, EXT_CLASS_NAME])
52
+ }
53
+ if (INCLUDE_STYLE) {
54
+ styleImport = `import './styles/${EXT_NAME}.css'
55
+ `
56
+ DIRS.push('styles')
57
+ const COMPONENT_CSS = `${EXT_COMPONENT_NAME} {
58
+
59
+ }`
60
+ FILES[`styles/${EXT_NAME}.css`] = INCLUDE_ELEMENT ? COMPONENT_CSS : ''
61
+ }
62
+ if (INCLUDE_SECTION) {
63
+ DIRS.push('sections')
64
+ const TAG = INCLUDE_ELEMENT ? EXT_COMPONENT_NAME : 'div'
65
+ FILES[`sections/${EXT_NAME}.liquid`] = starterContent(starterConfigs.section, [TAG, EXT_NAME])
66
+ }
67
+
68
+ if (INCLUDE_SNIPPET) {
69
+ DIRS.push('snippets')
70
+ FILES[`snippets/${EXT_NAME}.liquid`] = ''
71
+ }
72
+
73
+ FILES['index.js'] = `${scriptImport}${styleImport}`
74
+
75
+ loadDir({ location: FEATURES, DIRS, EXT_NAME, FILES })
76
+ }
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ element: require('./element'),
3
+ feature: require('./feature'),
4
+ starterContent: require('./starter-content'),
5
+ upperFirstLetter: require('./upper-first-letter.js'),
6
+ }
@@ -0,0 +1,24 @@
1
+ const chalk = require('chalk')
2
+ const fs = require('fs-extra')
3
+ const logSymbols = require('#LogSymbols')
4
+ const path = require('path')
5
+
6
+ module.exports = ({ location, DIRS, EXT_NAME, FILES }) => {
7
+ if (fs.existsSync(path.resolve(location, EXT_NAME))) {
8
+ console.log(logSymbols.error, chalk.red('Error:'), `Feature ${EXT_NAME} already exists. Rename or update feature directly instead.`)
9
+ process.exit()
10
+ }
11
+ fs.ensureDirSync(path.resolve(location, EXT_NAME))
12
+
13
+ for (const DIR of DIRS) {
14
+ fs.ensureDirSync(path.resolve(location, EXT_NAME, DIR))
15
+ }
16
+
17
+ for (const FILE of Object.entries(FILES)) {
18
+ fs.writeFileSync(path.resolve(location, EXT_NAME, FILE[0]), FILE[1], (err) => {
19
+ if (err) throw new Error(`${logSymbols.error} Error creating file ${FILE[0]}`)
20
+ })
21
+ }
22
+
23
+ console.log(`${logSymbols.success} ${chalk.green.bold(EXT_NAME)} created\n ${chalk.dim(location)}`)
24
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @file Returns starter content by file name, defaults to package file
3
+ * @param {string} fileName - The name of the starter file to return
4
+ * @param {array} args - An array of arguments to pass to the starter file
5
+ * @returns {string} - The starter file content
6
+ */
7
+
8
+ const path = require('path')
9
+ const fs = require('fs-extra')
10
+ const logSymbols = require('#LogSymbols')
11
+ const chalk = require('chalk')
12
+
13
+ module.exports = (fileName, args) => {
14
+ const fileExists = fs.existsSync(path.join(process.cwd(), '/utils/', fileName))
15
+ if (!fileExists) {
16
+ console.log(logSymbols.error, chalk.red('Error:'), `Starter file ${fileName} not found`)
17
+ process.exit()
18
+ }
19
+ const content = require(path.join(process.cwd(), '/utils/', fileName))
20
+ return content(...args)
21
+ }
@@ -0,0 +1,3 @@
1
+ module.exports = (string) => {
2
+ return string[0].toUpperCase() + string.substring(1)
3
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @file Creates a directory in theme-envy/features or theme-envy/elements with starter files to build your feature or element
3
+ * @param {string} type - The type of feature or element to create.
4
+ * @param {string} name - The name of the feature or element to create.
5
+ * @param {string} include - A comma separated list of files/directory to include in a new feature (not applicable to elements).
6
+ * @example
7
+ * npx theme-envy new <feature|element> <feature-name> [all|sections|snippets|schema|install|styles|config]
8
+ */
9
+
10
+ const chalk = require('chalk')
11
+ const logSymbols = require('#LogSymbols')
12
+ const { element, feature } = require('./functions')
13
+
14
+ module.exports = function(type, name, include) {
15
+ console.log(logSymbols.info, chalk.cyan('Creating new'), chalk.green.bold(name), chalk.underline.bold(type), chalk.cyan('...'))
16
+
17
+ switch (type) {
18
+ case 'feature':
19
+ feature(name, include)
20
+ break
21
+ case 'element':
22
+ element(name)
23
+ break
24
+ default:
25
+ console.log(logSymbols.error, chalk.red('Error:'), `Invalid type ${type}`)
26
+ process.exit()
27
+ }
28
+ }
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ starterConfigs: require('./starter-configs'),
3
+ }
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ config: 'starter-config.js',
3
+ install: 'starter-install.js',
4
+ schema: 'starter-schema.js',
5
+ element: 'starter-element.js',
6
+ section: 'starter-section.js',
7
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @file Check if the terminal supports unicode
3
+ */
4
+
5
+ module.exports = () => {
6
+ if (process.platform !== 'win32') {
7
+ return process.env.TERM !== 'linux' // Linux console (kernel)
8
+ }
9
+
10
+ return (
11
+ Boolean(process.env.CI) ||
12
+ Boolean(process.env.WT_SESSION) /* Windows Terminal */ ||
13
+ Boolean(process.env.TERMINUS_SUBLIME) /* Terminus (<0.2.27) */ ||
14
+ process.env.ConEmuTask === '{cmd::Cmder}' /* ConEmu and cmder */ ||
15
+ process.env.TERM_PROGRAM === 'Terminus-Sublime' ||
16
+ process.env.TERM_PROGRAM === 'vscode' ||
17
+ process.env.TERM === 'xterm-256color' ||
18
+ process.env.TERM === 'alacritty' ||
19
+ process.env.TERMINAL_EMULATOR === 'JetBrains-JediTerm'
20
+ )
21
+ }
@@ -0,0 +1,10 @@
1
+ module.exports = {
2
+ distClean: require('./functions/dist-clean.js'),
3
+ ensureDirectories: require('./functions/ensure-directories.js'),
4
+ findOrphans: require('./functions/find-orphans.js'),
5
+ globalThemeEnvy: require('./functions/global-theme-envy.js'),
6
+ liquidPrettify: require('./functions/liquid-prettify.js'),
7
+ liquidTree: require('./functions/liquid-tree'),
8
+ pullJson: require('./functions/pull-json.js'),
9
+ scaffoldNew: require('./functions/scaffold-new'),
10
+ }
package/index.js ADDED
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @file Theme Envy CLI
4
+ * @description Theme Envy CLI Tools
5
+ * @example npx theme-envy
6
+ */
7
+
8
+ const chalk = require('chalk')
9
+ const commander = require('commander')
10
+ const emoji = require('node-emoji')
11
+ const fs = require('fs-extra')
12
+ const program = new commander.Command()
13
+ const promptly = require('promptly')
14
+
15
+ const { directories } = require('#EnsureDirectories')
16
+ require('#Helpers/functions/global-theme-envy.js')
17
+
18
+ const themeEnvyCommands = {
19
+ build: require('#Build'),
20
+ clean: require('#Helpers/functions/dist-clean.js'),
21
+ convert: require('#Convert'),
22
+ dev: require('#Helpers/functions/dev.js'),
23
+ init: require('#Init'),
24
+ new: require('#Helpers/functions/scaffold-new/index.js'),
25
+ orphans: require('#Helpers/functions/find-orphans.js'),
26
+ 'pull-json': require('#Helpers/functions/pull-json.js'),
27
+ tree: require('#Helpers/functions/liquid-tree/index.js'),
28
+ }
29
+
30
+ const scriptMessage = (scriptName) => {
31
+ console.log(`\n ${emoji.get('rocket')} Shopify Theme Envy ${chalk.green.bgBlack.bold(`\n theme-envy ${scriptName}\n`)}`)
32
+ }
33
+
34
+ program
35
+ .command('init')
36
+ .description('Initialize a new Shopify theme project with Theme Envy directory structure')
37
+ .usage('[source] -e|--example -c|--convert')
38
+ .argument('[source]', 'Specify the path/git url to your theme source directory to process, if not provided will create the directory structure in /src')
39
+ .option('-s, --store <store>', 'Specify the myshopify domain for your store', 'my-store.myshopify.com')
40
+ .option('-c, --convert', 'Convert theme sections to features, add hooks, and install theme-envy feature on import')
41
+ .option('-e, --example', 'Output example feature structure and dummy files with readme documentation in each subdirectory')
42
+ .action(async (source, options, command) => {
43
+ scriptMessage(command.name())
44
+ if (!source) {
45
+ let choice = await promptly.choose(`${chalk.yellow('Would you like to:')}
46
+ a) Import an existing repo from a .git url
47
+ b) Initialize from a Shopify theme in the current directory, or initialize empty directories if no Shopify theme exists?
48
+
49
+ (a/b)?`, ['a', 'b', 'A', 'B'])
50
+ choice = choice.toLowerCase()
51
+ if (choice === 'a') {
52
+ const gitUrl = await promptly.prompt(chalk.yellow('Please provide the git url for your source theme:\n'), {
53
+ validator(value) {
54
+ if (value.includes('git@') || value.includes('https://')) return value
55
+ throw new Error(chalk.red.bold('Please enter a valid git url'))
56
+ }
57
+ })
58
+ if (gitUrl) {
59
+ source = gitUrl
60
+ }
61
+ }
62
+ }
63
+ // check for existing Shopify theme in root
64
+ const rootDirs = fs.readdirSync(process.cwd()).filter(res => !res.includes('.'))
65
+ const shopifyThemeExistsInRoot = directories.every(dir => rootDirs.includes(dir))
66
+ if (!options.convert && (shopifyThemeExistsInRoot || source)) {
67
+ const convert = await promptly.confirm(`
68
+ ${chalk.yellow('Do you want to convert the existing theme and install Theme Envy?')} ${chalk.green('(recommended)')}
69
+ This does the following things:
70
+ • Converts your theme sections to Theme Envy features (where possible)
71
+ • Adds ${chalk.cyan('{% hook %}')} tags to ${chalk.cyan('theme.liquid')}
72
+ • Installs the Theme Envy integration feature to ${chalk.cyan('theme-envy/features')}
73
+ (Y/n)`)
74
+ if (convert) {
75
+ options.convert = true
76
+ }
77
+ }
78
+ if (!options.example) {
79
+ const example = await promptly.confirm(`${chalk.yellow('Would you like to output an example Theme Envy "Feature"?')}\n(Y/n)?`)
80
+ if (example) {
81
+ options.example = true
82
+ }
83
+ }
84
+ if (options.store === 'my-store.myshopify.com') {
85
+ options.store = await promptly.prompt(chalk.yellow('Please provide the myshopify.com domain of your store (this can be changed later in theme.config.js):\n '))
86
+ }
87
+ themeEnvyCommands.init(source, options)
88
+ })
89
+
90
+ program
91
+ .command('dev')
92
+ .description('Start development process and sync with Shopify using the Shopify CLI')
93
+ .action((options, command) => {
94
+ scriptMessage(command.name())
95
+ themeEnvyCommands.dev()
96
+ })
97
+
98
+ program
99
+ .description('Theme Envy CLI Tools')
100
+ .addHelpText('beforeAll', `
101
+ ${emoji.get('green_heart') + chalk.green.bold(' Softlimit Shopify Theme Envy ') + emoji.get('green_heart')}
102
+ ${chalk.green.bold('===================================')}
103
+ `)
104
+ .addHelpText('afterAll', `
105
+ ${chalk.cyan.bold('===================================')}
106
+ ${emoji.get('thinking_face')} ${chalk.cyan.bold.underline('Need more help?')}
107
+ We are always looking for ways to improve our tools
108
+ and make your Shopify development life smoother.
109
+
110
+ Let us know what we missed! Create an issue on our github repo:
111
+ ${chalk.dim.underline('https://github.com/softlimit/shopify-env/issues')}
112
+ `)
113
+
114
+ program
115
+ .command('build')
116
+ .description('Build Shopify theme')
117
+ .usage('[development|production] -w|--watch')
118
+ .addArgument(new commander.Argument('[env]', 'Specify the build environment to run').choices(['development', 'production']))
119
+ .option('-w, --watch', 'Watch for changed files and update dist, serve with Shopify CLI')
120
+ .option('-v, --verbose', 'Show Tailwind and Webpack in output')
121
+ .action((env, options, command) => {
122
+ scriptMessage(command.name())
123
+ themeEnvyCommands.build(env, options)
124
+ })
125
+
126
+ program
127
+ .command('clean')
128
+ .description('Empty output directory')
129
+ .action((options, command) => {
130
+ scriptMessage(command.name())
131
+ themeEnvyCommands.clean()
132
+ })
133
+
134
+ program
135
+ .command('new')
136
+ .description('Create new Feature or Element from starter files')
137
+ .usage('<type> <name> [include]')
138
+ .addArgument(new commander.Argument('<type>', 'Define the type of scaffold to create').choices(['feature', 'element']))
139
+ .argument('<name>', 'Handleized name for the new element|feature')
140
+ .argument('[include]', 'Comma-separated list of starter directories and files to include in your scaffold. Defaults to all if not provided. all,config,install,sections,snippets,schema,styles')
141
+ .action((type, name, include, options, command) => {
142
+ scriptMessage(command.name())
143
+ if (name.includes(',') || name.includes(' ')) {
144
+ console.error(
145
+ chalk.red('Invalid Name:'),
146
+ 'Name argument should only include text and dashes, no commas or spaces. Rename with handle version to continue.'
147
+ )
148
+ return
149
+ }
150
+ themeEnvyCommands.new(type, name, include)
151
+ })
152
+
153
+ program
154
+ .command('find-orphans')
155
+ .description('Find unused snippets, partials, and assets in your Shopify theme')
156
+ .action((options, command) => {
157
+ scriptMessage(command.name())
158
+ themeEnvyCommands.orphans()
159
+ })
160
+
161
+ program
162
+ .command('pull-json')
163
+ .description('Pull json template, section, and settings_data files from theme using Shopify CLI')
164
+ .action((options, command) => {
165
+ scriptMessage(command.name())
166
+ themeEnvyCommands['pull-json']()
167
+ })
168
+
169
+ program
170
+ .command('tree')
171
+ .description('Display the dependency tree for a .liquid file')
172
+ .usage('[filepaths]')
173
+ .argument('[filepaths]', 'Project relative path(s) to .liquid files. Separate multiple paths with commas.')
174
+ .option('-v, --verbose', 'display as JS Object instead of formatted tree')
175
+ .action((filepath, options, command) => {
176
+ scriptMessage(command.name())
177
+ themeEnvyCommands.tree(filepath, options)
178
+ })
179
+
180
+ program
181
+ .command('convert')
182
+ .description('Convert an existing Shopify theme to Theme Envy directory structure')
183
+ .usage('[source] -a|--add-theme-envy-feature')
184
+ .argument('[source]', 'Specify the path to your theme source directory to process, defaults to project root ./src directory if not provided')
185
+ .option('-a, --add-theme-envy-feature', 'Add theme-envy feature and install to hook')
186
+ .action((source, options, command) => {
187
+ scriptMessage(command.name())
188
+ themeEnvyCommands.convert(source)
189
+ })
190
+ program.parse()
@@ -0,0 +1,6 @@
1
+ module.exports = [
2
+ {
3
+ hook: 'head-end',
4
+ content: "{% render 'theme-envy' %}"
5
+ }
6
+ ]
@@ -0,0 +1,21 @@
1
+ /*
2
+ Copies theme-envy features during theme-envy init
3
+ */
4
+ const fs = require('fs-extra')
5
+ const path = require('path')
6
+ const glob = require('glob')
7
+ const chalk = require('chalk')
8
+ const logSymbols = require('#LogSymbols')
9
+
10
+ module.exports = function({ dest }) {
11
+ const featuresDir = path.resolve(__dirname, './features')
12
+ const initFeatures = glob.sync(`${featuresDir}/*`).filter(ref => fs.statSync(ref).isDirectory() && ref !== featuresDir)
13
+ initFeatures.forEach(feature => {
14
+ const target = path.resolve(dest, './theme-envy/features', path.basename(feature))
15
+ if (fs.existsSync(target)) return
16
+ fs.copy(feature, target, err => {
17
+ if (err) return console.error(err)
18
+ console.log(`${logSymbols.success} ${chalk.green.bold(path.basename(feature))} feature copied and connected`)
19
+ })
20
+ })
21
+ }