/* eslint-disable */ // make sure generators are using the latest version of plugins, // and plugins are using the latest version of deps const fs = require('fs') const path = require('path') const chalk = require('chalk') const fetch = require('node-fetch') const semver = require('semver') const globby = require('globby') const { execSync } = require('child_process') const inquirer = require('inquirer') const readline = require('readline') const externalVueScopedPackages = { '@vue/test-utils': true, '@vue/eslint-config': true } const localPackageRE = /'(@vue\/(?:cli|eslint|babel)[\w-]+)': '\^([\w-.]+)'/g const versionCache = {} const getRemoteVersion = async (pkg) => { if (versionCache[pkg]) { return versionCache[pkg] } let res try { res = await fetch(`http://registry.npmjs.org/${pkg}/latest`).then(result => result.json()) } catch (e) { return } versionCache[pkg] = res.version return res.version } const getRemoteVersionSync = pkg => { if (versionCache[pkg]) { return versionCache[pkg] } const version = execSync(`npm view ${pkg} version`).toString().trim() versionCache[pkg] = version return version } const checkUpdate = (pkg, filePath, local, remote) => { if (remote !== local) { const isNewer = semver.gt(remote, local) if (!isNewer) { return false } const maybeBreaking = !semver.intersects(`^${local}`, `^${remote}`) console.log(genUpdateString(pkg, filePath, local, remote, maybeBreaking)) return true } } const checkUpdateAsync = async (pkg, filePath, local, remote) => { if (remote !== local) { const isNewer = semver.gt(remote, local) if (!isNewer) { return false } const maybeBreaking = !semver.intersects(`^${local}`, `^${remote}`) if (!maybeBreaking) { return true } const { shouldUpdate } = await inquirer.prompt([{ name: 'shouldUpdate', type: 'confirm', message: genUpdateString(pkg, filePath, local, remote, maybeBreaking) + `\n` + `Update this dependency?` }]) return shouldUpdate } } function genUpdateString (pkg, filePath, local, remote, maybeBreaking) { return `${chalk.cyan(pkg)}: ${local} => ${remote} ` + (maybeBreaking ? chalk.red.bold(`maybe breaking `) : ``) + chalk.gray(`(${path.relative(process.cwd(), filePath)})`) } const writeCache = {} const bufferWrite = (file, content) => { writeCache[file] = content } const flushWrite = () => { for (const file in writeCache) { fs.writeFileSync(file, writeCache[file]) } } async function syncDeps ({ local, version, skipPrompt }) { // 1. update all package deps const updatedDeps = new Set() if (!local) { console.log('Syncing remote deps...') const packages = await globby(['packages/@vue/*/package.json']) const resolvedPackages = (await Promise.all(packages.filter(filePath => { return filePath.match(/cli-service|cli-plugin|babel-preset|eslint-config/) }).concat('package.json').map(async (filePath) => { const pkg = require(path.resolve(__dirname, '../', filePath)) if (!pkg.dependencies) { return } const deps = pkg.dependencies const resolvedDeps = [] for (const dep in deps) { if (dep.match(/^@vue/) && !externalVueScopedPackages[dep]) { continue } let local = deps[dep] if (local.charAt(0) !== '^') { continue } local = local.replace(/^\^/, '') readline.clearLine(process.stdout) readline.cursorTo(process.stdout, 0) process.stdout.write(dep) const remote = await getRemoteVersion(dep) resolvedDeps.push({ dep, local, remote }) } return { pkg, filePath, resolvedDeps } }))).filter(_ => _) for (const { pkg, filePath, resolvedDeps } of resolvedPackages) { let isUpdated = false for (const { dep, local, remote } of resolvedDeps) { if (remote && await checkUpdateAsync(dep, filePath, local, remote)) { pkg.dependencies[dep] = `^${remote}` updatedDeps.add(dep) isUpdated = true } } if (isUpdated) { bufferWrite(filePath, JSON.stringify(pkg, null, 2) + '\n') } } } console.log('Syncing local deps...') const updatedRE = new RegExp(`'(${Array.from(updatedDeps).join('|')})': '\\^(\\d+\\.\\d+\\.\\d+[^']*)'`) const paths = await globby(['packages/@vue/**/*.js']) paths .filter(p => !/\/files\//.test(p) && !/\/node_modules/.test(p)) .forEach(filePath => { let isUpdated = false const makeReplacer = versionGetter => (_, pkg, curVersion) => { const targetVersion = versionGetter(pkg) if (!targetVersion) return _ if (checkUpdate(pkg, filePath, curVersion, targetVersion)) { isUpdated = true } return `'${pkg}': '^${targetVersion}'` } const localReplacer = makeReplacer( pkg => { try { // for eslint-config-* packages, only use version field from package.json // as they're published separately if (pkg.includes('eslint-config')) { return require(`../packages/${pkg}/package.json`).version } if (!semver.prerelease(version) && pkg.includes('@vue/cli-plugin')) { return `${semver.major(version)}.${semver.minor(version)}.0` } // otherwise, inline version takes priority return version || require(`../packages/${pkg}/package.json`).version } catch (e) {} } ) const remoteReplacer = makeReplacer(getRemoteVersionSync) const updated = fs.readFileSync(filePath, 'utf-8') // update @vue packages in this repo .replace(localPackageRE, localReplacer) // also update vue, vue-template-compiler, vuex, vue-router .replace(updatedRE, remoteReplacer) if (isUpdated) { bufferWrite(filePath, updated) } }) if (!Object.keys(writeCache).length) { return console.log(`All packages up-to-date.`) } if (skipPrompt) { flushWrite() return } const { yes } = await inquirer.prompt([{ name: 'yes', type: 'confirm', message: 'Commit above updates?' }]) if (yes) { flushWrite() } } exports.syncDeps = syncDeps if (!process.env.VUE_CLI_RELEASE) { const args = require('minimist')(process.argv.slice(2)) syncDeps(args).catch(err => { console.log(err) process.exit(1) }) }