@quasar/cli
Advanced tools
Comparing version 2.0.1 to 2.1.0
@@ -14,16 +14,2 @@ #!/usr/bin/env node | ||
import { createRequire } from 'module' | ||
const require = createRequire(import.meta.url) | ||
function getQuasarAppExecutable (which, root) { | ||
try { | ||
return require.resolve(which, { | ||
paths: [ root ] | ||
}) | ||
} | ||
catch (e) { | ||
return false | ||
} | ||
} | ||
let cmd = process.argv[ 2 ] | ||
@@ -33,23 +19,23 @@ | ||
process.argv.splice(2, 1) | ||
import(`../lib/cmd/create.js`) | ||
import('../lib/cmd/create.js') | ||
} | ||
else if (cmd === 'serve') { | ||
process.argv.splice(2, 1) | ||
import(`../lib/cmd/serve.js`) | ||
import('../lib/cmd/serve.js') | ||
} | ||
else if (cmd === 'upgrade') { | ||
process.argv.splice(2, 1) | ||
import(`../lib/cmd/upgrade.js`) | ||
import('../lib/cmd/upgrade.js') | ||
} | ||
else { | ||
const { getProjectRoot } = await import('../lib/get-project-root.js') | ||
const root = getProjectRoot() | ||
const { default: appPaths } = await import('../lib/app-paths.js') | ||
const { getPackagePath } = await import('../lib/get-package-path.js') | ||
const localFile = root | ||
const localFile = appPaths.appDir !== void 0 | ||
? ( | ||
getQuasarAppExecutable('@quasar/app-vite/bin/quasar', root) // Quasar 2.0 | ||
|| getQuasarAppExecutable('@quasar/app-webpack/bin/quasar', root) // Quasar 2.0 | ||
|| getQuasarAppExecutable('@quasar/app/bin/quasar', root) // legacy Quasar 1.0 & partial Quasar 2.0 | ||
|| getQuasarAppExecutable('quasar-cli/bin/quasar', root) // legacy Quasar <1.0 | ||
) | ||
getPackagePath('@quasar/app-vite/bin/quasar') // Quasar 2.0 | ||
|| getPackagePath('@quasar/app-webpack/bin/quasar') // Quasar 2.0 | ||
|| getPackagePath('@quasar/app/bin/quasar') // legacy Quasar 1.0 & partial Quasar 2.0 | ||
|| getPackagePath('quasar-cli/bin/quasar') // legacy Quasar <1.0 | ||
) | ||
: void 0 | ||
@@ -74,2 +60,5 @@ | ||
else { | ||
const { createRequire } = await import('node:module') | ||
const require = createRequire(import.meta.url) | ||
// local CLI is in legacy CJS format | ||
@@ -98,17 +87,15 @@ require(localFile) | ||
if (cmd === '-v' || cmd === '--version' || cmd === '-V') { | ||
console.log(pkg.version) | ||
console.log(`${ pkg.name } v${ pkg.version }`) | ||
process.exit(0) | ||
} | ||
await import(`../lib/cmd/help.js`) | ||
await import('../lib/cmd/help.js') | ||
const { red } = await import('kolorist') | ||
console.log(`\n ${ red(`Error`) } Unknown command "${ cmd }"`) | ||
const { fatal } = await import('../lib/logger.js') | ||
if (cmd.indexOf('-') === 0) { | ||
console.log(`\n ${ red(`Error`) } Command must come before the options`) | ||
fatal('Command must come before the options', 'Error') | ||
} | ||
console.log() | ||
process.exit(1) | ||
fatal(`Unknown command "${ cmd }"`, 'Error') | ||
} | ||
@@ -115,0 +102,0 @@ } |
@@ -91,2 +91,4 @@ | ||
const { fatal } = await import('../logger.js') | ||
const root = getAbsolutePath(argv._[ 0 ] || '.') | ||
@@ -139,3 +141,3 @@ const resolve = p => path.resolve(root, p) | ||
if (res.req.method === 'GET' && path === resolvedIndex) { | ||
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); | ||
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate') | ||
res.set('Pragma', 'no-cache') | ||
@@ -184,4 +186,3 @@ res.set('Expires', '0') | ||
if (!existsSync(file)) { | ||
console.error('Proxy definition file not found! ' + file) | ||
process.exit(1) | ||
fatal('Proxy definition file not found! ' + file) | ||
} | ||
@@ -249,4 +250,3 @@ file = await import(file) | ||
else { | ||
console.error('SSL key file not found!' + key) | ||
process.exit(1) | ||
fatal('SSL key file not found!' + key) | ||
} | ||
@@ -258,4 +258,3 @@ | ||
else { | ||
console.error('SSL cert file not found!' + cert) | ||
process.exit(1) | ||
fatal('SSL cert file not found!' + cert) | ||
} | ||
@@ -346,5 +345,3 @@ } | ||
catch (err) { | ||
console.error(' Cannot write certificate file ' + certPath) | ||
console.error(' Aborting...') | ||
process.exit(1) | ||
fatal(' Cannot write certificate file ' + certPath + '. Aborting...') | ||
} | ||
@@ -381,4 +378,4 @@ } | ||
] | ||
.filter(msg => msg) | ||
.map(msg => ' ' + msg[ 0 ].padEnd(20, '.') + ' ' + green(msg[ 1 ])) | ||
.filter(msg => msg) | ||
.map(msg => ' ' + msg[ 0 ].padEnd(20, '.') + ' ' + green(msg[ 1 ])) | ||
@@ -385,0 +382,0 @@ console.log('\n' + info.join('\n') + '\n') |
@@ -51,65 +51,19 @@ | ||
import fs from 'node:fs' | ||
import { join } from 'node:path' | ||
import { green, red } from 'kolorist' | ||
import { execSync } from 'node:child_process' | ||
const { getProjectRoot } = await import('../get-project-root.js') | ||
const root = getProjectRoot() | ||
import appPaths from '../app-paths.js' | ||
import { log, fatal, success } from '../logger.js' | ||
import { log, fatal } from '../logger.js' | ||
if (!root) { | ||
fatal(`⚠️ Error. This command must be executed inside a Quasar project folder only.`) | ||
if (appPaths.appDir === void 0) { | ||
fatal('This command must be executed inside a Quasar project folder only.', 'Error') | ||
} | ||
if (!fs.existsSync(join(root, 'node_modules'))) { | ||
fatal('⚠️ Please run "yarn" / "npm install" / "pnpm install" first\n') | ||
if (!fs.existsSync(appPaths.resolve.app('node_modules'))) { | ||
fatal('Please run "yarn" / "npm install" / "pnpm install" first\n', 'Error') | ||
} | ||
const pkg = JSON.parse( | ||
fs.readFileSync(join(root, 'package.json'), 'utf8') | ||
fs.readFileSync(appPaths.resolve.app('package.json'), 'utf8') | ||
) | ||
const versionRegex = /^(\d+)\.[\d]+\.[\d]+-?(alpha|beta|rc)?/ | ||
function getLatestVersion (packager, packageName, curVersion) { | ||
let versions | ||
try { | ||
versions = JSON.parse( | ||
execSync( | ||
`${ packager } info ${ packageName } versions --json`, | ||
{ stdio: [ 'ignore', 'pipe', 'pipe' ] } | ||
) | ||
) | ||
} | ||
catch (err) { | ||
return null | ||
} | ||
if (versions.type === 'error') { | ||
return null | ||
} | ||
if (packager === 'yarn') { | ||
versions = versions.data | ||
} | ||
if (curVersion === null) { | ||
return versions[ versions.length - 1 ] | ||
} | ||
const [ , major, prerelease ] = curVersion.match(versionRegex) | ||
const majorSyntax = argv.major ? `(\\d+)` : major | ||
const regex = new RegExp( | ||
prerelease || argv.prerelease | ||
? `^${ majorSyntax }\\.(\\d+)\\.(\\d+)-?(alpha|beta|rc)?` | ||
: `^${ majorSyntax }\\.(\\d+)\\.(\\d+)$` | ||
) | ||
versions = versions.filter(version => regex.test(version)) | ||
return versions[ versions.length - 1 ] | ||
} | ||
const deps = { | ||
@@ -120,7 +74,7 @@ dependencies: [], | ||
const packager = (await import('../node-packager.js')).getNodePackager(root) | ||
const getPackageJson = (await import('../get-package-json.js')).getPackageJson(root) | ||
import { nodePackager } from '../node-packager.js' | ||
import { getPackageJson } from '../get-package-json.js' | ||
console.log() | ||
log(`Gathering information with ${ packager }...`) | ||
log(`Gathering information with ${ nodePackager.name }...`) | ||
console.log() | ||
@@ -139,3 +93,3 @@ | ||
const json = getPackageJson(packageName) | ||
const curVersion = json | ||
const currentVersion = json | ||
? json.version | ||
@@ -145,3 +99,3 @@ : null | ||
// q/app v3 has been renamed to q/app-webpack | ||
if (packageName === '@quasar/app' && curVersion && curVersion.startsWith('3.')) { | ||
if (packageName === '@quasar/app' && currentVersion && currentVersion.startsWith('3.')) { | ||
removeDeprecatedAppPkg = true | ||
@@ -151,13 +105,18 @@ packageName = '@quasar/app-webpack' | ||
const latestVersion = getLatestVersion(packager, packageName, curVersion) | ||
const latestVersion = nodePackager.getPackageLatestVersion({ | ||
packageName, | ||
currentVersion, | ||
majorVersion: argv.major, | ||
preReleaseVersion: argv.prerelease | ||
}) | ||
const current = curVersion === null | ||
const current = currentVersion === null | ||
? red('Missing!') | ||
: curVersion | ||
: currentVersion | ||
if (latestVersion === null) { | ||
console.log(` ${ green(packageName) }: ${ current } → ${ red('Skipping!') }`) | ||
console.log(` (⚠️ NPM registry server returned an error, so we cannot detect latest version)`) | ||
console.log(' (⚠️ NPM registry server returned an error, so we cannot detect latest version)') | ||
} | ||
else if (curVersion !== latestVersion) { | ||
else if (currentVersion !== latestVersion) { | ||
deps[ type ].push({ | ||
@@ -178,3 +137,3 @@ packageName, | ||
// @quasar/wizard AE. Do not change under any circumstances. | ||
console.log(' Congrats! All Quasar packages are up to date.\n') | ||
log('Congrats! All Quasar packages are up to date.\n') | ||
process.exit(0) | ||
@@ -196,17 +155,12 @@ } | ||
const { default: { removeSync } } = await import('fs-extra') | ||
const { spawn } = await import('../spawn.js') | ||
if (removeDeprecatedAppPkg === true) { | ||
const params = packager === 'yarn' || packager === 'pnpm' | ||
? [ 'remove', '@quasar/app' ] | ||
: [ 'uninstall', '--save-dev', '@quasar/app' ] | ||
console.log() | ||
spawn(packager, params, root) | ||
nodePackager.uninstallPackage('@quasar/app', { displayName: 'deprecated @quasar/app' }) | ||
// need to delete the package otherwise | ||
// installing the new version might fail on Windows; | ||
removeSync(join(root, 'node_modules/@quasar/app')) | ||
removeSync(appPaths.resolve.app('node_modules/@quasar/app')) | ||
const tsConfigFile = join(root, 'tsconfig.json') | ||
const tsConfigFile = appPaths.resolve.app('tsconfig.json') | ||
if (fs.existsSync(tsConfigFile) === true) { | ||
@@ -221,3 +175,3 @@ const content = fs.readFileSync(tsConfigFile, 'utf-8') | ||
const quasarDTsFile = join(root, 'src/quasar.d.ts') | ||
const quasarDTsFile = appPaths.resolve.app('src/quasar.d.ts') | ||
if (fs.existsSync(quasarDTsFile) === true) { | ||
@@ -238,9 +192,3 @@ const content = fs.readFileSync(quasarDTsFile, 'utf-8') | ||
const params = packager === 'yarn' | ||
? (type === 'devDependencies' ? [ 'add', '--dev' ] : [ 'add' ]) | ||
: ( | ||
packager === 'pnpm' | ||
? [ 'install' ] | ||
: [ 'install', `--save${ type === 'devDependencies' ? '-dev' : '' }` ] // npm | ||
) | ||
const packageList = [] | ||
@@ -250,3 +198,3 @@ deps[ type ].forEach(dep => { | ||
// installing the new version might fail on Windows | ||
removeSync(join(root, 'node_modules', dep.packageName)) | ||
removeSync(appPaths.resolve.app('node_modules/' + dep.packageName)) | ||
@@ -259,10 +207,18 @@ const pinned = /^\d/.test( | ||
params.push(`${ dep.packageName }@${ pinned ? '' : '^' }${ dep.latestVersion }`) | ||
packageList.push( | ||
`${ dep.packageName }@${ pinned ? '' : '^' }${ dep.latestVersion }` | ||
) | ||
}) | ||
console.log() | ||
spawn(packager, params, root) | ||
nodePackager.installPackage( | ||
packageList, | ||
{ | ||
displayName: packageList.join(' ') + (type === 'devDependencies' ? ' as devDependencies' : ''), | ||
isDevDependency: type === 'devDependencies' | ||
} | ||
) | ||
} | ||
console.log() | ||
log('Successfully upgraded Quasar packages.\n') | ||
success('Successfully upgraded Quasar packages.\n') |
@@ -1,15 +0,23 @@ | ||
import { createRequire } from 'module' | ||
const require = createRequire(import.meta.url) | ||
export function getPackageJson (root) { | ||
return function (pkgName) { | ||
try { | ||
return require( | ||
require.resolve(`${ pkgName }/package.json`, { | ||
paths: [ root ] | ||
}) | ||
import { readFileSync } from 'node:fs' | ||
import appPaths from './app-paths.js' | ||
import { getPackagePath } from './get-package-path.js' | ||
/** | ||
* Get package.json of a host package. | ||
* Don't use it for direct dependencies of this project. | ||
*/ | ||
export function getPackageJson (pkgName, folder = appPaths.appDir) { | ||
try { | ||
return JSON.parse( | ||
readFileSync( | ||
getPackagePath(`${ pkgName }/package.json`, folder), | ||
'utf-8' | ||
) | ||
} | ||
catch (e) {} | ||
) | ||
} | ||
catch (_) { | ||
/* do and return nothing */ | ||
} | ||
} |
@@ -1,46 +0,130 @@ | ||
// Adapted from Vue CLI v2 "init" command | ||
import { gray, white, red } from 'kolorist' | ||
import { | ||
bgGreen, green, | ||
inverse, | ||
bgRed, red, | ||
bgYellow, yellow, | ||
black, white, | ||
underline | ||
} from 'kolorist' | ||
import { format } from 'node:util' | ||
import readline from 'node:readline' | ||
/** | ||
* Prefix. | ||
* Pills | ||
*/ | ||
const prefix = ' Quasar CLI' | ||
const sep = gray('·') | ||
export const successPill = msg => bgGreen(black(` ${ msg } `)) | ||
export const infoPill = msg => inverse(` ${ msg } `) | ||
export const errorPill = msg => bgRed(white(` ${ msg } `)) | ||
export const warningPill = msg => bgYellow(black(` ${ msg } `)) | ||
/** | ||
* Log a `message` to the console. | ||
* | ||
* @param {String} message | ||
* Main approach - App CLI related | ||
*/ | ||
export function log (...args) { | ||
const msg = format.apply(format, args) | ||
console.log(white(prefix), sep, msg) | ||
export const dot = '•' | ||
const banner = 'Global Quasar CLI ' + dot | ||
const greenBanner = green(banner) | ||
const redBanner = red(banner) | ||
const yellowBanner = yellow(banner) | ||
const tipBanner = `${ green('Global Quasar CLI') } ${ dot } ${ successPill('TIP') } ${ dot } 🚀 ` | ||
export const clearConsole = process.stdout.isTTY | ||
? () => { | ||
// Fill screen with blank lines. Then move to 0 (beginning of visible part) and clear it | ||
const blank = '\n'.repeat(process.stdout.rows) | ||
console.log(blank) | ||
readline.cursorTo(process.stdout, 0, 0) | ||
readline.clearScreenDown(process.stdout) | ||
} | ||
: () => {} | ||
export function tip (msg) { | ||
console.log(msg ? ` ${ tipBanner } ${ format(msg) }` : '') | ||
} | ||
export function log (msg) { | ||
console.log(msg ? ` ${ greenBanner } ${ format(msg) }` : '') | ||
} | ||
export function warn (msg, pill) { | ||
if (msg !== void 0) { | ||
const pillBanner = pill !== void 0 | ||
? bgYellow(black('', pill, '')) + ' ' | ||
: '' | ||
console.warn(` ${ yellowBanner } ⚠️ ${ pillBanner }${ format(msg) }`) | ||
} | ||
else { | ||
console.warn() | ||
} | ||
} | ||
export function fatal (msg, pill) { | ||
if (msg !== void 0) { | ||
const pillBanner = pill !== void 0 | ||
? errorPill(pill) + ' ' | ||
: '' | ||
console.error(`\n ${ redBanner } ⚠️ ${ pillBanner }${ format(msg) }\n`) | ||
} | ||
else { | ||
console.error() | ||
} | ||
process.exit(1) | ||
} | ||
/** | ||
* Log an error `message` to the console and exit. | ||
* | ||
* @param {String} message | ||
* Extended approach - Compilation status & pills | ||
*/ | ||
export function fatal (...args) { | ||
if (args[ 0 ] instanceof Error) args[ 0 ] = args[ 0 ].message.trim() | ||
const msg = format.apply(format, args) | ||
console.error(red(prefix), sep, msg) | ||
process.exit(1) | ||
export function success (msg, title = 'SUCCESS') { | ||
console.log(` ${ greenBanner } ${ successPill(title) } ${ green(dot + ' ' + format(msg)) }`) | ||
} | ||
export function getSuccess (msg, title) { | ||
return ` ${ greenBanner } ${ successPill(title) } ${ green(dot + ' ' + format(msg)) }` | ||
} | ||
export function info (msg, title = 'INFO') { | ||
console.log(` ${ greenBanner } ${ infoPill(title) } ${ green(dot) } ${ format(msg) }`) | ||
} | ||
export function getInfo (msg, title) { | ||
return ` ${ greenBanner } ${ infoPill(title) } ${ green(dot) } ${ format(msg) }` | ||
} | ||
export function error (msg, title = 'ERROR') { | ||
console.log(` ${ redBanner } ${ errorPill(title) } ${ red(dot + ' ' + format(msg)) }`) | ||
} | ||
export function getError (msg, title = 'ERROR') { | ||
return ` ${ redBanner } ${ errorPill(title) } ${ red(dot + ' ' + format(msg)) }` | ||
} | ||
export function warning (msg, title = 'WARNING') { | ||
console.log(` ${ yellowBanner } ${ warningPill(title) } ${ yellow(dot + ' ' + format(msg)) }`) | ||
} | ||
export function getWarning (msg, title = 'WARNING') { | ||
return ` ${ yellowBanner } ${ warningPill(title) } ${ yellow(dot + ' ' + format(msg)) }` | ||
} | ||
/** | ||
* Log a success `message` to the console and exit. | ||
* | ||
* @param {String} message | ||
* Progress related | ||
*/ | ||
export function success (...args) { | ||
const msg = format.apply(format, args) | ||
console.log(white(prefix), sep, msg) | ||
export function progress (msg, token) { | ||
const parseMsg = token !== void 0 | ||
? text => text.replace('___', underline(green(token))) | ||
: text => text | ||
info(parseMsg(msg), 'WAIT') | ||
const startTime = Date.now() | ||
return msg => { | ||
const diffTime = +new Date() - startTime | ||
success(`${ parseMsg(msg) } ${ dot } ${ diffTime }ms`, 'DONE') | ||
log() | ||
} | ||
} |
import os from 'os' | ||
export function getExternalNetworkInterface () { | ||
const | ||
networkInterfaces = os.networkInterfaces() | ||
const devices = [] | ||
const networkInterfaces = os.networkInterfaces() | ||
const devices = [] | ||
@@ -8,0 +7,0 @@ for (const deviceName of Object.keys(networkInterfaces)) { |
@@ -1,42 +0,342 @@ | ||
import { existsSync } from 'node:fs' | ||
import { join } from 'node:path' | ||
import { sync as spawn } from 'cross-spawn' | ||
import { fatal } from './logger.js' | ||
import fs from 'node:fs' | ||
import { normalize, join, sep } from 'node:path' | ||
import { sync as crossSpawnSync } from 'cross-spawn' | ||
function isInstalled (cmd) { | ||
try { | ||
return spawn(cmd, [ '--version' ]).status === 0 | ||
import appPaths from './app-paths.js' | ||
import { log, fatal } from './logger.js' | ||
import { spawnSync } from './spawn.js' | ||
const versionRegex = /^(\d+)\.[\d]+\.[\d]+-?(alpha|beta|rc)?/ | ||
function run ({ name, params, cwd, onFail, env = 'development' }) { | ||
spawnSync( | ||
name, | ||
params.filter(param => typeof param === 'string' && param.length !== 0), | ||
{ cwd: cwd || appPaths.appDir, env: { ...process.env, NODE_ENV: env } }, | ||
onFail | ||
) | ||
} | ||
class PackageManager { | ||
/** | ||
* To be declared by subclasses | ||
*/ | ||
name = 'unknown' | ||
lockFile = 'unknown' | ||
getInstallParams (_env) { | ||
return [] | ||
} | ||
catch (err) { | ||
getInstallPackageParams (_names, _isDev) { | ||
return [] | ||
} | ||
getUninstallPackageParams (_names) { | ||
return [] | ||
} | ||
getPackageVersionList (_packageName) { | ||
return null | ||
} | ||
/** | ||
* Implementation of the actual package manager | ||
*/ | ||
get majorVersion () { | ||
try { | ||
const child = crossSpawnSync(this.name, [ '--version' ]) | ||
if (child.status === 0) { | ||
const version = String(child.output[ 1 ]).trim() | ||
return parseInt(version.split('.')[ 0 ], 10) | ||
} | ||
} | ||
catch (_) { | ||
/* do nothing; we return null below */ | ||
} | ||
return null | ||
} | ||
isUsed () { | ||
let directory = appPaths.appDir | ||
// Recursively checks for presence of the lock file by traversing | ||
// the directory tree up to the root | ||
while (directory.length && directory[ directory.length - 1 ] !== sep) { | ||
if (fs.existsSync(join(directory, this.lockFile))) { | ||
return true | ||
} | ||
directory = normalize(join(directory, '..')) | ||
} | ||
return false | ||
} | ||
isInstalled () { | ||
return this.majorVersion !== null | ||
} | ||
install ({ cwd, params, displayName, env = 'development' } = {}) { | ||
displayName = displayName ? displayName + ' ' : '' | ||
log(`Installing ${ displayName }dependencies...`) | ||
run({ | ||
name: this.name, | ||
params: params && params.length !== 0 | ||
? params | ||
: this.getInstallParams(env), | ||
cwd, | ||
env, | ||
onFail: () => fatal(`Failed to install ${ displayName }dependencies`, 'FAIL') | ||
}) | ||
} | ||
installPackage (name, { cwd, displayName = name, isDevDependency = false } = {}) { | ||
log(`Installing ${ displayName }...`) | ||
run({ | ||
name: this.name, | ||
params: this.getInstallPackageParams(Array.isArray(name) ? name : [ name ], isDevDependency), | ||
cwd, | ||
onFail: () => fatal(`Failed to install ${ displayName }`, 'FAIL') | ||
}) | ||
} | ||
uninstallPackage (name, { cwd, displayName = name } = {}) { | ||
log(`Uninstalling ${ displayName }...`) | ||
run({ | ||
name: this.name, | ||
params: this.getUninstallPackageParams(Array.isArray(name) ? name : [ name ]), | ||
cwd, | ||
onFail: () => fatal(`Failed to uninstall ${ displayName }`, 'FAIL') | ||
}) | ||
} | ||
getPackageLatestVersion ({ | ||
packageName, | ||
currentVersion = null, | ||
majorVersion = false, | ||
preReleaseVersion = false | ||
}) { | ||
const versionList = this.getPackageVersionList(packageName) | ||
if (versionList === null) { | ||
return null | ||
} | ||
if (currentVersion === null) { | ||
return versionList[ versionList.length - 1 ] | ||
} | ||
const [ , major, prerelease ] = currentVersion.match(versionRegex) | ||
const majorSyntax = majorVersion ? '(\\d+)' : major | ||
const regex = new RegExp( | ||
prerelease || preReleaseVersion | ||
? `^${ majorSyntax }\\.(\\d+)\\.(\\d+)-?(alpha|beta|rc)?` | ||
: `^${ majorSyntax }\\.(\\d+)\\.(\\d+)$` | ||
) | ||
const list = versionList.filter(version => regex.test(version)) | ||
return list[ list.length - 1 ] || null | ||
} | ||
} | ||
export function getNodePackager (root) { | ||
if (existsSync(join(root, 'yarn.lock'))) { | ||
return 'yarn' | ||
class Npm extends PackageManager { | ||
name = 'npm' | ||
lockFile = 'package-lock.json' | ||
getInstallParams (env) { | ||
if (env === 'development') { | ||
return [ 'install' ] | ||
} | ||
return this.majorVersion >= 9 | ||
? [ 'install' ] // env will be set to production | ||
: [ 'install', '--production' ] | ||
} | ||
if (existsSync(join(root, 'pnpm-lock.yaml'))) { | ||
return 'pnpm' | ||
getInstallPackageParams (names, isDevDependency) { | ||
return [ | ||
'install', | ||
isDevDependency ? '--save-dev' : '', | ||
...names | ||
] | ||
} | ||
if (existsSync(join(root, 'package-lock.json'))) { | ||
return 'npm' | ||
getUninstallPackageParams (names) { | ||
return [ 'uninstall', ...names ] | ||
} | ||
if (isInstalled('yarn')) { | ||
return 'yarn' | ||
getPackageVersionList (packageName) { | ||
let versionList | ||
try { | ||
const child = crossSpawnSync(this.name, [ 'info', packageName, 'versions', '--json' ]) | ||
if (child.status === 0) { | ||
versionList = JSON.parse(String(child.output[ 1 ])) | ||
} | ||
} | ||
catch (_) { | ||
return null | ||
} | ||
if (!versionList) return null | ||
if (versionList.type === 'error') return null | ||
versionList = versionList.data | ||
return !versionList || !Array.isArray(versionList) || versionList.length === 0 | ||
? null | ||
: versionList | ||
} | ||
} | ||
if (isInstalled('npm')) { | ||
return 'npm' | ||
class Yarn extends PackageManager { | ||
name = 'yarn' | ||
lockFile = 'yarn.lock' | ||
getInstallParams (env) { | ||
if (env === 'development') { | ||
return [ 'install' ] | ||
} | ||
return this.majorVersion >= 2 | ||
? [ | ||
'workspaces', | ||
'focus', | ||
'--all', | ||
'--production' | ||
] | ||
: [ | ||
'install', | ||
'--production' | ||
] | ||
} | ||
if (isInstalled('pnpm')) { | ||
return 'pnpm' | ||
getInstallPackageParams (names, isDevDependency) { | ||
return [ | ||
'add', | ||
isDevDependency ? '--dev' : '', | ||
...names | ||
] | ||
} | ||
fatal('⚠️ Please install Yarn or Pnpm or NPM before running this command.\n') | ||
getUninstallPackageParams (names) { | ||
return [ 'remove', ...names ] | ||
} | ||
getPackageVersionList (packageName) { | ||
let versionList | ||
try { | ||
const params = this.majorVersion >= 2 | ||
? [ 'npm', 'info', packageName, '--fields', 'versions', '--json' ] | ||
: [ 'info', packageName, 'versions', '--json' ] | ||
const child = crossSpawnSync(this.name, params) | ||
if (child.status === 0) { | ||
versionList = JSON.parse(String(child.output[ 1 ])) | ||
} | ||
} | ||
catch (_) { | ||
return null | ||
} | ||
if (!versionList) return null | ||
if (versionList.type === 'error') return null | ||
versionList = versionList[ this.majorVersion >= 2 ? 'versions' : 'data' ] | ||
return !versionList || !Array.isArray(versionList) || versionList.length === 0 | ||
? null | ||
: versionList | ||
} | ||
} | ||
class Pnpm extends PackageManager { | ||
name = 'pnpm' | ||
lockFile = 'pnpm-lock.yaml' | ||
getInstallParams (env) { | ||
return env === 'development' | ||
? [ 'install' ] | ||
: [ 'install', '--prod' ] | ||
} | ||
getInstallPackageParams (names, isDevDependency) { | ||
return [ | ||
'add', | ||
isDevDependency ? '--save-dev' : '', | ||
...names | ||
] | ||
} | ||
getUninstallPackageParams (names) { | ||
return [ 'remove', ...names ] | ||
} | ||
getPackageVersionList (packageName) { | ||
let versionList | ||
try { | ||
const child = crossSpawnSync(this.name, [ 'info', packageName, 'versions', '--json' ]) | ||
if (child.status === 0) { | ||
versionList = JSON.parse(String(child.output[ 1 ])) | ||
} | ||
} | ||
catch (_) { | ||
return null | ||
} | ||
if (!versionList) return null | ||
if (versionList.type === 'error') return null | ||
versionList = versionList.data | ||
return !versionList || !Array.isArray(versionList) || versionList.length === 0 | ||
? null | ||
: versionList | ||
} | ||
} | ||
/** | ||
* @returns {PackageManager} | ||
*/ | ||
function getPackager () { | ||
const yarn = new Yarn() | ||
if (yarn.isUsed()) { | ||
return yarn | ||
} | ||
const pnpm = new Pnpm() | ||
if (pnpm.isUsed()) { | ||
return pnpm | ||
} | ||
const npm = new Npm() | ||
if (npm.isUsed()) { | ||
return npm | ||
} | ||
if (yarn.isInstalled()) { | ||
return yarn | ||
} | ||
if (pnpm.isInstalled()) { | ||
return pnpm | ||
} | ||
if (npm.isInstalled()) { | ||
return npm | ||
} | ||
fatal('Please install Yarn, PNPM, or NPM before running this command.\n') | ||
} | ||
export const nodePackager = getPackager() |
@@ -5,4 +5,4 @@ // version-check | ||
const major = parseInt(version[ 0 ].replace(/\D/g, ''), 10) | ||
const minor = parseInt(version[ 1 ].replace(/\D/g,''), 10) | ||
const patch = parseInt(version[ 2 ].replace(/\D/g,''), 10) | ||
const minor = parseInt(version[ 1 ].replace(/\D/g, ''), 10) | ||
const patch = parseInt(version[ 2 ].replace(/\D/g, ''), 10) | ||
@@ -9,0 +9,0 @@ const min = { |
@@ -1,18 +0,68 @@ | ||
import { log, fatal } from './logger.js' | ||
import { default as crossSpawn } from 'cross-spawn' | ||
export function spawn (cmd, params, cwd) { | ||
log(`Running "${ cmd } ${ params.join(' ') }"`) | ||
console.log() | ||
import crossSpawn from 'cross-spawn' | ||
import { log, warn, fatal } from './logger.js' | ||
/* | ||
Returns pid, takes onClose | ||
*/ | ||
export function spawn (cmd, params, opts, onClose) { | ||
if (!cmd) { | ||
fatal('Command name was not available. Please run again.') | ||
} | ||
const targetFolder = opts && opts.cwd | ||
? ` in ${ opts.cwd }` | ||
: '' | ||
log(`Running "${ cmd } ${ params.join(' ') }"${ targetFolder }`) | ||
log() | ||
const runner = crossSpawn( | ||
cmd, | ||
params, | ||
{ stdio: 'inherit', stdout: 'inherit', stderr: 'inherit', ...opts } | ||
) | ||
runner.on('close', code => { | ||
log() | ||
if (code) { | ||
log(`Command "${ cmd }" failed with exit code: ${ code }`) | ||
} | ||
onClose && onClose(code) | ||
}) | ||
if (opts.detach === true) { | ||
runner.unref() | ||
} | ||
return runner.pid | ||
} | ||
/* | ||
Returns nothing, takes onFail | ||
*/ | ||
export function spawnSync (cmd, params, opts, onFail) { | ||
const targetFolder = opts && opts.cwd | ||
? ` in ${ opts.cwd }` | ||
: '' | ||
log(`[sync] Running "${ cmd } ${ params.join(' ') }"${ targetFolder }`) | ||
log() | ||
const runner = crossSpawn.sync( | ||
cmd, | ||
params, | ||
{ stdio: 'inherit', stdout: 'inherit', stderr: 'inherit', cwd } | ||
{ stdio: 'inherit', stdout: 'inherit', stderr: 'inherit', ...opts } | ||
) | ||
if (runner.status || runner.error) { | ||
console.log() | ||
fatal(`⚠️ Command "${ cmd }" failed with exit code: ${ runner.status }`) | ||
warn() | ||
warn(`Command "${ cmd }" failed with exit code: ${ runner.status }`) | ||
if (runner.status === null) { | ||
warn(`Please globally install "${ cmd }"`) | ||
} | ||
onFail && onFail() | ||
process.exit(1) | ||
} | ||
} |
{ | ||
"name": "@quasar/cli", | ||
"version": "2.0.1", | ||
"version": "2.1.0", | ||
"description": "Quasar Framework - the Global CLI", | ||
@@ -67,3 +67,6 @@ "type": "module", | ||
"eslint": "^8.31.0", | ||
"eslint-plugin-n": "^15.6.1" | ||
"eslint-config-standard": "^17.0.0", | ||
"eslint-plugin-import": "^2.27.5", | ||
"eslint-plugin-n": "^15.6.1", | ||
"eslint-plugin-promise": "^6.1.1" | ||
}, | ||
@@ -70,0 +73,0 @@ "publishConfig": { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
43850
20
1252
8
5