@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,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,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,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,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,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,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
|
+
}
|
package/helpers/index.js
ADDED
@@ -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,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
|
+
}
|