@quasar/icongenie 5.0.1 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,148 +1,16 @@
1
- const iosIconRegex = /AppIcon-(\d+\.?\d?)x?(\d+\.?\d?)?@?(\d+)?x?-?\d?\.png/
2
-
3
- function getAndroidIcons(entries) {
4
- const list = []
5
-
6
- entries.forEach(entry => {
7
- const icon = {
8
- generator: 'png',
9
- folder: `src-capacitor/android/app/src/main/res/mipmap-${entry[0]}`
10
- }
11
-
12
- list.push({
13
- ...icon,
14
- name: 'ic_launcher_foreground.png',
15
- sizes: [entry[2]]
16
- })
17
-
18
- list.push({
19
- ...icon,
20
- name: 'ic_launcher_round.png',
21
- sizes: [entry[1]]
22
- })
23
-
24
- list.push({
25
- ...icon,
26
- name: 'ic_launcher.png',
27
- sizes: [entry[1]]
28
- })
29
- })
30
-
31
- return list
32
- }
33
-
34
- function getAndroidSplashscreen(entries) {
35
- const list = []
36
-
37
- entries.forEach(entry => {
38
- const icon = {
39
- generator: 'splashscreen',
40
- name: 'splash.png'
41
- }
42
-
43
- list.push({
44
- ...icon,
45
- folder: `src-capacitor/android/app/src/main/res/drawable-land-${entry[0]}`,
46
- sizes: [[entry[1], entry[2]]]
47
- })
48
-
49
- list.push({
50
- ...icon,
51
- folder: `src-capacitor/android/app/src/main/res/drawable-port-${entry[0]}`,
52
- sizes: [[entry[2], entry[1]]]
53
- })
54
- })
55
-
56
- return list
57
- }
58
-
59
- function getIosIcon(name) {
60
- const [, size, , multiplier] = name.match(iosIconRegex)
61
-
62
- return {
63
- generator: 'png',
64
- name,
65
- folder: 'src-capacitor/ios/App/App/Assets.xcassets/AppIcon.appiconset',
66
- sizes: [
67
- multiplier
68
- ? Number.parseFloat(size) * Number.parseInt(multiplier, 10)
69
- : Number.parseFloat(size)
70
- ],
71
- background: true
72
- }
73
- }
74
-
75
1
  export default [
76
- /***************
77
- *** Android ***
78
- ***************/
79
-
80
- ...getAndroidIcons([
81
- ['hdpi', 49, 162],
82
- ['mdpi', 48, 108],
83
- ['xhdpi', 96, 216],
84
- ['xxhdpi', 144, 324],
85
- ['xxxhdpi', 192, 432]
86
- ]),
87
-
88
2
  {
89
- generator: 'splashscreen',
90
- name: 'splash.png',
91
- folder: 'src-capacitor/android/app/src/main/res/drawable',
92
- sizes: [[480, 320]]
93
- },
94
-
95
- ...getAndroidSplashscreen([
96
- ['mdpi', 480, 320],
97
- ['hdpi', 800, 480],
98
- ['xhdpi', 1280, 720],
99
- ['xxhdpi', 1600, 960],
100
- ['xxxhdpi', 1920, 1280]
101
- ]),
102
-
103
- /**************
104
- **** iOS *****
105
- **************/
106
-
107
- ...[
108
- 'AppIcon-20x20@1x.png',
109
- 'AppIcon-20x20@2x-1.png',
110
- 'AppIcon-20x20@2x.png',
111
- 'AppIcon-20x20@3x.png',
112
- 'AppIcon-29x29@1x.png',
113
- 'AppIcon-29x29@2x-1.png',
114
- 'AppIcon-29x29@2x.png',
115
- 'AppIcon-29x29@3x.png',
116
- 'AppIcon-40x40@1x.png',
117
- 'AppIcon-40x40@2x-1.png',
118
- 'AppIcon-40x40@2x.png',
119
- 'AppIcon-40x40@3x.png',
120
- 'AppIcon-60x60@2x.png',
121
- 'AppIcon-60x60@3x.png',
122
- 'AppIcon-76x76@1x.png',
123
- 'AppIcon-76x76@2x.png',
124
- 'AppIcon-83.5x83.5@2x.png',
125
- 'AppIcon-512@2x.png'
126
- ].map(getIosIcon),
127
-
128
- {
129
- generator: 'splashscreen',
130
- name: 'splash-2732x2732-1.png',
131
- folder: 'src-capacitor/ios/App/App/Assets.xcassets/Splash.imageset',
132
- sizes: [2732]
133
- },
134
-
135
- {
136
- generator: 'splashscreen',
137
- name: 'splash-2732x2732-2.png',
138
- folder: 'src-capacitor/ios/App/App/Assets.xcassets/Splash.imageset',
139
- sizes: [2732]
3
+ // tray icon (all platforms)
4
+ generator: 'png',
5
+ name: 'icon.png',
6
+ folder: `src-capacitor/assets`,
7
+ sizes: [1024]
140
8
  },
141
9
 
142
10
  {
143
11
  generator: 'splashscreen',
144
- name: 'splash-2732x2732.png',
145
- folder: 'src-capacitor/ios/App/App/Assets.xcassets/Splash.imageset',
12
+ name: 'splash.png',
13
+ folder: 'src-capacitor/assets',
146
14
  sizes: [2732]
147
15
  }
148
16
  ]
@@ -1,8 +1,8 @@
1
1
  import { existsSync } from 'node:fs'
2
2
  import { resolveDir } from '../../utils/app-paths.js'
3
3
 
4
- const dir = existsSync(resolveDir('src-electron/electron-assets'))
5
- ? 'electron-assets' // q/app-vite v3+
4
+ const dir = existsSync(resolveDir('src-electron/electron-assets/icons'))
5
+ ? 'electron-assets/icons' // q/app-vite v3+
6
6
  : existsSync(resolveDir('src-electron/icons'))
7
7
  ? 'icons' // q/app-vite v2 or q/app-webpack v4
8
8
  : 'electron-assets' // fallback to q/app-webpack v3 specs
@@ -12,21 +12,21 @@ export default [
12
12
  // macos (embedded icons)
13
13
  generator: 'icns',
14
14
  name: 'icon.icns',
15
- folder: `src-electron/${dir}/icons`
15
+ folder: `src-electron/${dir}`
16
16
  },
17
17
 
18
18
  {
19
19
  // windows (embedded icon)
20
20
  generator: 'ico',
21
21
  name: 'icon.ico',
22
- folder: `src-electron/${dir}/icons`
22
+ folder: `src-electron/${dir}`
23
23
  },
24
24
 
25
25
  {
26
26
  // tray icon (all platforms)
27
27
  generator: 'png',
28
28
  name: 'icon.png',
29
- folder: `src-electron/${dir}/icons`,
29
+ folder: `src-electron/${dir}`,
30
30
  sizes: [512]
31
31
  }
32
32
  ]
@@ -52,20 +52,38 @@ export default [
52
52
  },
53
53
 
54
54
  ...[
55
- [1290, 2796, 3, '<!-- iPhone 14 Pro, 14 Pro Max -->'],
56
- [1179, 2556, 3, '<!-- iPhone 14, 14 Pro -->'],
57
- [1284, 2778, 3, '<!-- iPhone 12 Pro Max, 13 Pro Max -->'],
58
- [1170, 2532, 3, '<!-- iPhone 12, 12 Pro, 13, 13 Pro -->'],
59
- [1080, 2340, 3, '<!-- iPhone 13 Mini -->'],
60
- [828, 1792, 2, '<!-- iPhone XR, 11 -->'],
61
- [1125, 2436, 3, '<!-- iPhone X, XS, 12 mini, 11 Pro -->'],
62
- [1242, 2688, 3, '<!-- iPhone XS Max, 11 Pro Max -->'],
63
- [750, 1334, 2, '<!-- iPhone 8, 7, 6s, 6 -->'],
55
+ [1320, 2868, 3, '<!-- iPhone 17 Pro Max, 16 Pro Max -->'],
56
+ [1260, 2736, 3, '<!-- iPhone 17 Air -->'],
57
+ [1206, 2622, 3, '<!-- iPhone 17 Pro, 17, 16 Pro -->'],
58
+ [1290, 2796, 3, '<!-- iPhone 16 Plus, 15 Pro Max, 15 Plus, 14 Pro Max -->'],
59
+ [1179, 2556, 3, '<!-- iPhone 16, 15 Pro, 15, 14 Pro -->'],
60
+ [1284, 2778, 3, '<!-- iPhone 14 Plus, 13 Pro Max, 12 Pro Max -->'],
61
+ [1170, 2532, 3, '<!-- iPhone 17e, 16e, 14, 13 Pro, 13, 12 Pro, 12 -->'],
62
+ [1080, 2340, 3, '<!-- iPhone 13 mini, 12 mini -->'],
63
+ [1242, 2688, 3, '<!-- iPhone 11 Pro Max, XS Max -->'],
64
+ [1125, 2436, 3, '<!-- iPhone 11 Pro, X, XS -->'],
65
+ [828, 1792, 2, '<!-- iPhone 11, XR -->'],
64
66
  [1242, 2208, 3, '<!-- iPhone 8 Plus, 7 Plus, 6s Plus, 6 Plus -->'],
65
- [1620, 2160, 2, '<!-- iPad 10.2" -->'],
66
- [1536, 2048, 2, '<!-- iPad Mini, Air, 9.7" -->'],
67
- [1668, 2224, 2, '<!-- iPad Pro 10.5" -->'],
68
- [1668, 2388, 2, '<!-- iPad Pro 11" -->'],
69
- [2048, 2732, 2, '<!-- iPad Pro 12.9" -->']
67
+ [750, 1334, 2, '<!-- iPhone 8, 7, 6s, 6, SE (2nd & 3rd gen) -->'],
68
+
69
+ [2064, 2752, 2, '<!-- iPad Pro 13" (M4) -->'],
70
+ [2048, 2732, 2, '<!-- iPad Pro 12.9", iPad Air 13" (M2) -->'],
71
+ [1668, 2420, 2, '<!-- iPad Pro 11" (M4) -->'],
72
+ [1668, 2388, 2, '<!-- iPad Pro 11" (M1/M2) -->'],
73
+ [
74
+ 1640,
75
+ 2360,
76
+ 2,
77
+ '<!-- iPad 11" (11th gen), iPad 10.9" (10th gen), iPad Air 11" (M2), iPad Air 10.9" -->'
78
+ ],
79
+ [1668, 2224, 2, '<!-- iPad Pro 10.5", iPad Air 3rd Gen -->'],
80
+ [1620, 2160, 2, '<!-- iPad 10.2" (7th, 8th, 9th gen) -->'],
81
+ [1488, 2266, 2, '<!-- iPad Mini (6th & 7th gen) -->'],
82
+ [
83
+ 1536,
84
+ 2048,
85
+ 2,
86
+ '<!-- iPad Mini (up to 5th gen), iPad Air 9.7", iPad 9.7" -->'
87
+ ]
70
88
  ].map(getAppleLaunch)
71
89
  ]
@@ -1,8 +1,10 @@
1
+ import { mountCapacitor } from './mount-capacitor.js'
1
2
  import { isCordovaFile, mountCordova, verifyCordova } from './mount-cordova.js'
2
3
  import { mountTag } from './mount-tag.js'
3
4
 
4
- export function mount(files) {
5
- mountCordova(files)
5
+ export async function mount(files) {
6
+ await mountCapacitor(files)
7
+ await mountCordova(files)
6
8
  mountTag(files)
7
9
  }
8
10
 
@@ -0,0 +1,74 @@
1
+ import { existsSync, readFileSync } from 'node:fs'
2
+
3
+ import { resolveDir } from '../utils/app-paths.js'
4
+ import { warn } from '../utils/logger.js'
5
+ import { spawnSync } from '../utils/spawn-sync.js'
6
+ import { createInstance } from '../utils/package-manager.js'
7
+
8
+ const srcCapacitorDir = resolveDir('src-capacitor')
9
+
10
+ async function installCapacitorAssets() {
11
+ const pkgPath = resolveDir('src-capacitor/package.json')
12
+
13
+ // malformed /src-capacitor...
14
+ if (!existsSync(pkgPath)) return false
15
+
16
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
17
+
18
+ if (
19
+ !pkg.devDependencies?.['@capacitor/assets'] &&
20
+ !pkg.dependencies?.['@capacitor/assets']
21
+ ) {
22
+ const pm = createInstance(srcCapacitorDir)
23
+ if (typeof pm === 'string') {
24
+ warn(pm)
25
+ return false
26
+ }
27
+
28
+ const success = await pm.installPackage('@capacitor/assets', {
29
+ isDevDependency: true,
30
+ allowBuilds: true
31
+ })
32
+
33
+ if (!success) {
34
+ warn()
35
+ warn('Failed to install @capacitor/assets. Please do it manually.')
36
+ return false
37
+ }
38
+ }
39
+
40
+ if (
41
+ !pkg.dependencies?.['@capacitor/splash-screen'] &&
42
+ !pkg.devDependencies?.['@capacitor/splash-screen']
43
+ ) {
44
+ const pm = createInstance(srcCapacitorDir)
45
+ if (typeof pm === 'string') {
46
+ warn(pm)
47
+ return false
48
+ }
49
+
50
+ const success = await pm.installPackage('@capacitor/splash-screen')
51
+
52
+ if (!success) {
53
+ warn()
54
+ warn('Failed to install @capacitor/splash-screen. Please do it manually.')
55
+ }
56
+ }
57
+
58
+ return true
59
+ }
60
+
61
+ export async function mountCapacitor() {
62
+ const hasInstalled = await installCapacitorAssets()
63
+ if (!hasInstalled) return
64
+
65
+ const success = await spawnSync('npx', ['@capacitor/assets', 'generate'], {
66
+ cwd: srcCapacitorDir
67
+ })
68
+
69
+ if (!success) {
70
+ warn()
71
+ warn('Failed to run @capacitor/assets. Please do it manually.')
72
+ console.log(' -> /src-capacitor: $ npx @capacitor/assets generate\n')
73
+ }
74
+ }
@@ -128,7 +128,7 @@ function hasDeepProp(target, ...args) {
128
128
  return true
129
129
  }
130
130
 
131
- function installSplashscreenPlugin() {
131
+ async function installSplashscreenPlugin() {
132
132
  const pkgPath = resolveDir('src-cordova/package.json')
133
133
 
134
134
  // malformed /src-cordova...
@@ -144,29 +144,26 @@ function installSplashscreenPlugin() {
144
144
  return
145
145
  }
146
146
 
147
- log(`Installing cordova-plugin-splashscreen...`)
148
-
149
- spawnSync(
147
+ const hasInstalled = await spawnSync(
150
148
  'cordova',
151
149
  ['plugin', 'add', 'cordova-plugin-splashscreen'],
152
150
  {
153
151
  cwd: srcCordovaDir
154
- },
155
- () => {
156
- warn()
157
- warn(
158
- 'Failed to install cordova-plugin-splashscreen. Please do it manually.'
159
- )
160
- console.log(
161
- ' -> /src-cordova: $ cordova plugin add cordova-plugin-splashscreen\n'
162
- )
163
152
  }
164
153
  )
165
154
 
166
- console.log()
155
+ if (!hasInstalled) {
156
+ warn()
157
+ warn(
158
+ 'Failed to install cordova-plugin-splashscreen. Please do it manually.'
159
+ )
160
+ console.log(
161
+ ' -> /src-cordova: $ cordova plugin add cordova-plugin-splashscreen\n'
162
+ )
163
+ }
167
164
  }
168
165
 
169
- export function mountCordova(files) {
166
+ export async function mountCordova(files) {
170
167
  if (existsSync(cordovaConfigXml)) {
171
168
  const cordovaFiles = getCordovaFiles(files)
172
169
 
@@ -176,7 +173,7 @@ export function mountCordova(files) {
176
173
  )
177
174
 
178
175
  if (hasSplashscreen) {
179
- installSplashscreenPlugin()
176
+ await installSplashscreenPlugin()
180
177
  }
181
178
 
182
179
  updateConfigXml(cordovaFiles, hasSplashscreen)
@@ -114,9 +114,7 @@ async function generateFromProfile(profile) {
114
114
  printBanner(assetsOf, params)
115
115
 
116
116
  return Promise.all(uniqueFiles.map(file => generateFile(file, fileOptions)))
117
- .then(() => {
118
- mount(uniqueFiles)
119
- })
117
+ .then(() => mount(uniqueFiles))
120
118
  .then(() => uniqueFiles.length)
121
119
  }
122
120
 
@@ -1,4 +1,5 @@
1
1
  import { green, red } from 'kolorist'
2
+ import { isCI } from 'ci-info'
2
3
 
3
4
  const banner = '*'
4
5
 
@@ -17,3 +18,56 @@ export function fatal(msg) {
17
18
  console.error(msg ? ` ${warnBanner} ⚠️ ${msg}` : '')
18
19
  process.exit(1)
19
20
  }
21
+
22
+ /**
23
+ * Alternate Screen
24
+ */
25
+
26
+ export function enterAlternateScreen(message) {
27
+ if (isCI) return
28
+
29
+ // Enter Alternate Screen Buffer (hides current terminal history)
30
+ process.stdout.write('\u001B[?1049h')
31
+ // Move cursor to top left
32
+ process.stdout.write('\u001B[H')
33
+
34
+ if (message) console.log(`>>> ${message}\n`)
35
+ }
36
+
37
+ export function exitAlternateScreen() {
38
+ if (isCI) return
39
+ process.stdout.write('\u001B[?1049l')
40
+ }
41
+
42
+ export function waitForKey() {
43
+ // Are we in a real terminal?
44
+ // If not (e.g., CI pipeline), resolve immediately so the script doesn't hang forever.
45
+ if (isCI) return Promise.resolve()
46
+
47
+ const { stdin } = process
48
+ process.stdout.write('Press any key to continue...')
49
+
50
+ const { promise, resolve: resolvePromise } = Promise.withResolvers()
51
+
52
+ // Enable raw mode to bypass the 'Enter' key requirement
53
+ stdin.setRawMode(true)
54
+ stdin.resume()
55
+ stdin.setEncoding('utf8')
56
+
57
+ const handleKey = key => {
58
+ stdin.off('data', handleKey)
59
+ stdin.setRawMode(false)
60
+ stdin.pause()
61
+
62
+ // Explicitly handle Ctrl+C
63
+ if (key === '\u0003') {
64
+ console.log('\nProcess cancelled by user (Ctrl+C)\n')
65
+ process.exit(1)
66
+ }
67
+
68
+ resolvePromise()
69
+ }
70
+
71
+ stdin.on('data', handleKey)
72
+ return promise
73
+ }
@@ -0,0 +1,241 @@
1
+ import fs from 'node:fs'
2
+ import { join, normalize, sep } from 'node:path'
3
+ import { sync as crossSpawnSync } from 'cross-spawn'
4
+
5
+ import { spawnSync } from './spawn-sync.js'
6
+
7
+ // returns a Promise!
8
+ function run({ name, params, cwd, env = 'development' }) {
9
+ return spawnSync(
10
+ name,
11
+ params.filter(param => typeof param === 'string' && param.length !== 0),
12
+ { cwd, env: { NODE_ENV: env } }
13
+ )
14
+ }
15
+
16
+ function getMajorVersion(name) {
17
+ try {
18
+ const child = crossSpawnSync(name, ['--version'])
19
+ if (child.status === 0) {
20
+ const version = String(child.output[1]).trim()
21
+ return Number.parseInt(version.split('.')[0], 10)
22
+ }
23
+ } catch {
24
+ /* do nothing; we return null below */
25
+ }
26
+
27
+ return null
28
+ }
29
+
30
+ class PackageManager {
31
+ appDir
32
+
33
+ constructor(appDir) {
34
+ this.appDir = appDir
35
+ }
36
+
37
+ /**
38
+ * To be declared by subclasses
39
+ */
40
+ name = 'unknown'
41
+ lockFiles = ['unknown']
42
+
43
+ getInstallParams(/* env */) {
44
+ return []
45
+ }
46
+
47
+ getInstallPackageParams(/* names, isDev, allowBuilds */) {
48
+ return []
49
+ }
50
+
51
+ getUninstallPackageParams(/* names */) {
52
+ return []
53
+ }
54
+
55
+ /**
56
+ * Implementation of the actual package manager
57
+ */
58
+
59
+ majorVersion = null
60
+ cachedIsInstalled = null
61
+
62
+ isInstalled() {
63
+ if (this.cachedIsInstalled !== null) {
64
+ return this.cachedIsInstalled
65
+ }
66
+
67
+ this.majorVersion = getMajorVersion(this.name)
68
+ this.cachedIsInstalled = this.majorVersion !== null
69
+
70
+ return this.cachedIsInstalled
71
+ }
72
+
73
+ // returns a Promise!
74
+ install({ cwd = this.appDir, params, env = 'development' } = {}) {
75
+ return run({
76
+ name: this.name,
77
+ params:
78
+ params && params.length !== 0 ? params : this.getInstallParams(env),
79
+ cwd,
80
+ env
81
+ })
82
+ }
83
+
84
+ // returns a Promise!
85
+ installPackage(
86
+ name,
87
+ { cwd = this.appDir, isDevDependency = false, allowBuilds = false } = {}
88
+ ) {
89
+ return run({
90
+ name: this.name,
91
+ params: this.getInstallPackageParams(
92
+ Array.isArray(name) ? name : [name],
93
+ isDevDependency,
94
+ allowBuilds
95
+ ),
96
+ cwd
97
+ })
98
+ }
99
+
100
+ // returns a Promise!
101
+ uninstallPackage(name, { cwd = this.appDir } = {}) {
102
+ return run({
103
+ name: this.name,
104
+ params: this.getUninstallPackageParams(
105
+ Array.isArray(name) ? name : [name]
106
+ ),
107
+ cwd
108
+ })
109
+ }
110
+ }
111
+
112
+ class Npm extends PackageManager {
113
+ name = 'npm'
114
+ lockFiles = ['package-lock.json']
115
+
116
+ getInstallParams(env) {
117
+ if (env === 'development') {
118
+ return ['install']
119
+ }
120
+
121
+ return this.majorVersion >= 9
122
+ ? ['install'] // env will be set to production
123
+ : ['install', '--production']
124
+ }
125
+
126
+ getInstallPackageParams(names, isDevDependency) {
127
+ return ['install', isDevDependency ? '--save-dev' : '', ...names]
128
+ }
129
+
130
+ getUninstallPackageParams(names) {
131
+ return ['uninstall', ...names]
132
+ }
133
+ }
134
+
135
+ class Yarn extends PackageManager {
136
+ name = 'yarn'
137
+ lockFiles = ['yarn.lock']
138
+
139
+ getInstallParams(env) {
140
+ if (env === 'development') {
141
+ return ['install']
142
+ }
143
+
144
+ return this.majorVersion >= 2
145
+ ? ['workspaces', 'focus', '--all', '--production']
146
+ : ['install', '--production']
147
+ }
148
+
149
+ getInstallPackageParams(names, isDevDependency) {
150
+ return ['add', isDevDependency ? '--dev' : '', ...names]
151
+ }
152
+
153
+ getUninstallPackageParams(names) {
154
+ return ['remove', ...names]
155
+ }
156
+ }
157
+
158
+ class Pnpm extends PackageManager {
159
+ name = 'pnpm'
160
+ lockFiles = ['pnpm-lock.yaml']
161
+
162
+ getInstallParams(env) {
163
+ return env === 'development' ? ['install'] : ['install', '--prod']
164
+ }
165
+
166
+ getInstallPackageParams(names, isDevDependency, allowBuilds) {
167
+ return [
168
+ 'add',
169
+ isDevDependency ? '--save-dev' : '',
170
+ allowBuilds ? '--dangerously-allow-all-builds' : '',
171
+ ...names
172
+ ]
173
+ }
174
+
175
+ getUninstallPackageParams(names) {
176
+ return ['remove', ...names]
177
+ }
178
+ }
179
+
180
+ class Bun extends PackageManager {
181
+ name = 'bun'
182
+ lockFiles = ['bun.lock', 'bun.lockb']
183
+
184
+ getInstallParams(env) {
185
+ return env === 'development' ? ['install'] : ['install', '--production']
186
+ }
187
+
188
+ getInstallPackageParams(names, isDevDependency) {
189
+ return ['add', isDevDependency ? '--dev' : '', ...names]
190
+ }
191
+
192
+ getUninstallPackageParams(names) {
193
+ return ['remove', ...names]
194
+ }
195
+ }
196
+
197
+ /**
198
+ * @returns {PackageManager}
199
+ */
200
+ function getProjectPackageManager(packageManagersList, dir) {
201
+ // Recursively checks for presence of the lock file by traversing
202
+ // the dir tree up to the root
203
+ while (dir.length !== 0 && dir.at(-1) !== sep) {
204
+ for (const pm of packageManagersList) {
205
+ if (pm.lockFiles.some(lockFile => fs.existsSync(join(dir, lockFile)))) {
206
+ return pm
207
+ }
208
+ }
209
+
210
+ dir = normalize(join(dir, '..'))
211
+ }
212
+ }
213
+
214
+ export function createInstance(appDir) {
215
+ const packageManagersList = [
216
+ new Yarn(appDir),
217
+ new Pnpm(appDir),
218
+ new Npm(appDir),
219
+ new Bun(appDir)
220
+ ]
221
+
222
+ const projectPackageManager = getProjectPackageManager(
223
+ packageManagersList,
224
+ appDir
225
+ )
226
+
227
+ // if the project folder uses a supported package manager
228
+ // and it is installed on this machine then use it
229
+ if (projectPackageManager !== void 0 && projectPackageManager.isInstalled()) {
230
+ return projectPackageManager
231
+ }
232
+
233
+ // otherwise, use the first installed package manager
234
+ for (const pm of packageManagersList) {
235
+ if (pm.isInstalled()) {
236
+ return pm
237
+ }
238
+ }
239
+
240
+ return 'Please install PNPM (recommended), Yarn, NPM or Bun before running this command.'
241
+ }
@@ -1,18 +1,58 @@
1
1
  import crossSpawn from 'cross-spawn'
2
+ import { relative } from 'node:path'
3
+ import { isCI } from 'ci-info'
2
4
 
3
- import { log, warn } from './logger.js'
5
+ import {
6
+ enterAlternateScreen,
7
+ exitAlternateScreen,
8
+ log,
9
+ waitForKey
10
+ } from './logger.js'
4
11
 
5
- export function spawnSync(cmd, params, opts, onFail) {
6
- log(`[sync] Running "${cmd} ${params.join(' ')}"\n`)
12
+ const extraEnvParams = isCI
13
+ ? {}
14
+ : { FORCE_COLOR: process.env.FORCE_COLOR ?? '1' }
15
+
16
+ /*
17
+ Returns nothing, takes onFail
18
+ */
19
+ export async function spawnSync(cmd, params, opts) {
20
+ const targetFolder = opts?.cwd
21
+ ? ` in /${relative(process.cwd(), opts.cwd)}`
22
+ : ''
23
+ const message = `Running "${cmd} ${params.join(' ')}"${targetFolder}`
24
+
25
+ log(message)
26
+ enterAlternateScreen(message)
7
27
 
8
28
  const runner = crossSpawn.sync(cmd, params, {
9
29
  stdio: 'inherit',
10
- ...opts
30
+ ...opts,
31
+ env: { ...process.env, ...extraEnvParams, ...opts.env }
11
32
  })
12
33
 
13
- if (runner.status || runner.error) {
14
- warn()
15
- warn(`Command "${cmd}" failed with exit code: ${runner.status}`)
16
- onFail?.()
34
+ if (runner.error || runner.status || runner.status === null) {
35
+ const errorMessage =
36
+ runner.status === null || runner.error?.code === 'ENOENT'
37
+ ? `Command "${cmd}" not found! Please install it globally.`
38
+ : runner.status
39
+ ? `Command "${cmd} ${params.join(' ')}" failed with exit code: ${runner.status}`
40
+ : `Command "${cmd} ${params.join(' ')}" failed!`
41
+
42
+ const msg = `⚠️ ⚠️ ⚠️ ${errorMessage} ⚠️ ⚠️ ⚠️ `
43
+
44
+ console.log()
45
+ console.error(msg)
46
+ console.log()
47
+
48
+ await waitForKey()
49
+ exitAlternateScreen()
50
+ console.error(msg)
51
+
52
+ return false
17
53
  }
54
+
55
+ exitAlternateScreen()
56
+ log(`Executed "${cmd} ${params.join(' ')}"${targetFolder}`)
57
+ return true
18
58
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quasar/icongenie",
3
- "version": "5.0.1",
3
+ "version": "6.0.0",
4
4
  "description": "A Quasar tool for generating all your Quasar App's icons and splashscreens",
5
5
  "keywords": [
6
6
  "bex",
@@ -42,6 +42,7 @@
42
42
  "LICENSE"
43
43
  ],
44
44
  "type": "module",
45
+ "main": "bin/icongenie.js",
45
46
  "publishConfig": {
46
47
  "access": "public"
47
48
  },