From db9be90e4a79cc4dba169d174af46d497356a926 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 11 Jan 2018 10:05:37 -0500 Subject: [PATCH] fix executable linking on windows for real --- .../__tests__/eslintPlugin.spec.js | 7 +- packages/@vue/cli-shared-utils/env.js | 26 +++++ packages/@vue/cli-shared-utils/index.js | 103 +----------------- packages/@vue/cli-shared-utils/linkBin.js | 27 +++++ packages/@vue/cli-shared-utils/logger.js | 61 +++++++++++ packages/@vue/cli-shared-utils/package.json | 1 + packages/@vue/cli-shared-utils/validate.js | 12 ++ packages/@vue/cli/lib/Creator.js | 2 +- packages/@vue/cli/lib/util/setupDevProject.js | 9 +- 9 files changed, 138 insertions(+), 110 deletions(-) create mode 100644 packages/@vue/cli-shared-utils/env.js create mode 100644 packages/@vue/cli-shared-utils/linkBin.js create mode 100644 packages/@vue/cli-shared-utils/logger.js create mode 100644 packages/@vue/cli-shared-utils/validate.js diff --git a/packages/@vue/cli-plugin-eslint/__tests__/eslintPlugin.spec.js b/packages/@vue/cli-plugin-eslint/__tests__/eslintPlugin.spec.js index 17b42603f9..cb8087e2e9 100644 --- a/packages/@vue/cli-plugin-eslint/__tests__/eslintPlugin.spec.js +++ b/packages/@vue/cli-plugin-eslint/__tests__/eslintPlugin.spec.js @@ -1,7 +1,7 @@ jest.setTimeout(30000) -const fs = require('fs') const path = require('path') +const { linkBin } = require('@vue/cli-shared-utils') const create = require('@vue/cli-test-utils/createTestProject') const runSilently = fn => { @@ -37,10 +37,9 @@ test('should work', async () => { require('yorkie/src/install')(path.join(project.dir, 'node_modules')) // since yorkie isn't actually installed in the test project, we need to // symlink it - fs.symlinkSync( + linkBin( path.resolve(require.resolve('yorkie/src/install'), '../../'), - path.join(project.dir, 'node_modules', 'yorkie'), - 'junction' // needed for windows + path.join(project.dir, 'node_modules', 'yorkie') ) }) const hook = await read('.git/hooks/pre-commit') diff --git a/packages/@vue/cli-shared-utils/env.js b/packages/@vue/cli-shared-utils/env.js new file mode 100644 index 0000000000..788359b09e --- /dev/null +++ b/packages/@vue/cli-shared-utils/env.js @@ -0,0 +1,26 @@ +const { execSync } = require('child_process') + +// env detection +exports.hasYarn = (() => { + if (process.env.VUE_CLI_TEST) { + return true + } + try { + execSync('yarnpkg --version', { stdio: 'ignore' }) + return true + } catch (e) { + return false + } +})() + +exports.hasGit = () => { + if (process.env.VUE_CLI_TEST) { + return true + } + try { + execSync('git --version', { stdio: 'ignore' }) + return true + } catch (e) { + return false + } +} diff --git a/packages/@vue/cli-shared-utils/index.js b/packages/@vue/cli-shared-utils/index.js index 1c20dfb506..e614fc5ae6 100644 --- a/packages/@vue/cli-shared-utils/index.js +++ b/packages/@vue/cli-shared-utils/index.js @@ -1,99 +1,4 @@ -const joi = require('joi') -const chalk = require('chalk') -const spinner = require('./spinner') -const readline = require('readline') -const { execSync } = require('child_process') -const padStart = require('string.prototype.padstart') - -const format = (label, msg) => { - return msg.split('\n').map((line, i) => { - return i === 0 - ? `${label} ${line}` - : padStart(line, chalk.reset(label).length) - }).join('\n') -} - -Object.assign(exports, spinner) - -exports.log = msg => console.log(msg || '') - -exports.info = msg => { - console.log(format(chalk.bgBlue.black(' INFO '), msg)) -} - -exports.done = msg => { - console.log(format(chalk.bgGreen.black(' DONE '), msg)) -} - -exports.warn = msg => { - console.warn(format(chalk.bgYellow.black(' WARN '), chalk.yellow(msg))) -} - -exports.error = msg => { - console.error(format(chalk.bgRed(' ERROR '), chalk.red(msg))) - if (msg instanceof Error) { - console.error(msg.stack) - } -} - -exports.clearConsole = title => { - if (process.stdout.isTTY) { - const blank = '\n'.repeat(process.stdout.rows) - console.log(blank) - readline.cursorTo(process.stdout, 0, 0) - readline.clearScreenDown(process.stdout) - if (title) { - console.log(title) - } - } -} - -// silent all logs except errors during tests and keep record -if (process.env.VUE_CLI_TEST) { - const logs = {} - Object.keys(exports).forEach(key => { - if (key !== 'error') { - exports[key] = (...args) => { - if (!logs[key]) logs[key] = [] - logs[key].push(args) - } - } - }) - exports.logs = logs -} - -// proxy to joi for option validation -exports.createSchema = fn => fn(joi) - -exports.validate = (obj, schema, options = {}) => { - joi.validate(obj, schema, options, err => { - if (err) { - throw err - } - }) -} - -// env detection -exports.hasYarn = (() => { - if (process.env.VUE_CLI_TEST) { - return true - } - try { - execSync('yarnpkg --version', { stdio: 'ignore' }) - return true - } catch (e) { - return false - } -})() - -exports.hasGit = () => { - if (process.env.VUE_CLI_TEST) { - return true - } - try { - execSync('git --version', { stdio: 'ignore' }) - return true - } catch (e) { - return false - } -} +Object.assign(exports, require('./env')) +Object.assign(exports, require('./logger')) +Object.assign(exports, require('./validate')) +Object.assign(exports, require('./linkBin')) diff --git a/packages/@vue/cli-shared-utils/linkBin.js b/packages/@vue/cli-shared-utils/linkBin.js new file mode 100644 index 0000000000..04f06d3023 --- /dev/null +++ b/packages/@vue/cli-shared-utils/linkBin.js @@ -0,0 +1,27 @@ +/* eslint-disable vue-libs/no-async-functions */ + +// cross-platform executable link, mostly for Windows + +const fs = require('fs') +const path = require('path') +const { promisify } = require('util') + +const chmod = promisify(fs.chmod) +const symlink = promisify(fs.symlink) +const mkdirp = promisify(require('mkdirp')) +const cmdShim = promisify(require('cmd-shim')) + +exports.linkBin = async (src, dest) => { + if (!process.env.VUE_CLI_TEST && !process.env.VUE_CLI_DEBUG) { + throw new Error(`linkBin should only be used during tests or debugging.`) + } + if (process.platform === 'win32') { + // not doing mutex lock because this is only used in dev and the + // src will not be modified + await cmdShim(src, dest) + } else { + await mkdirp(path.dirname(dest)) + await symlink(src, dest) + await chmod(dest, '755') + } +} diff --git a/packages/@vue/cli-shared-utils/logger.js b/packages/@vue/cli-shared-utils/logger.js new file mode 100644 index 0000000000..55ceb41ed9 --- /dev/null +++ b/packages/@vue/cli-shared-utils/logger.js @@ -0,0 +1,61 @@ +const chalk = require('chalk') +const spinner = require('./spinner') +const readline = require('readline') +const padStart = require('string.prototype.padstart') + +Object.assign(exports, spinner) + +const format = (label, msg) => { + return msg.split('\n').map((line, i) => { + return i === 0 + ? `${label} ${line}` + : padStart(line, chalk.reset(label).length) + }).join('\n') +} + +exports.log = msg => console.log(msg || '') + +exports.info = msg => { + console.log(format(chalk.bgBlue.black(' INFO '), msg)) +} + +exports.done = msg => { + console.log(format(chalk.bgGreen.black(' DONE '), msg)) +} + +exports.warn = msg => { + console.warn(format(chalk.bgYellow.black(' WARN '), chalk.yellow(msg))) +} + +exports.error = msg => { + console.error(format(chalk.bgRed(' ERROR '), chalk.red(msg))) + if (msg instanceof Error) { + console.error(msg.stack) + } +} + +exports.clearConsole = title => { + if (process.stdout.isTTY) { + const blank = '\n'.repeat(process.stdout.rows) + console.log(blank) + readline.cursorTo(process.stdout, 0, 0) + readline.clearScreenDown(process.stdout) + if (title) { + console.log(title) + } + } +} + +// silent all logs except errors during tests and keep record +if (process.env.VUE_CLI_TEST) { + const logs = {} + Object.keys(exports).forEach(key => { + if (key !== 'error') { + exports[key] = (...args) => { + if (!logs[key]) logs[key] = [] + logs[key].push(args) + } + } + }) + exports.logs = logs +} diff --git a/packages/@vue/cli-shared-utils/package.json b/packages/@vue/cli-shared-utils/package.json index fdfc2d3d74..a593fedc9a 100644 --- a/packages/@vue/cli-shared-utils/package.json +++ b/packages/@vue/cli-shared-utils/package.json @@ -20,6 +20,7 @@ "homepage": "https://github.com/vuejs/vue-cli/packages/@vue/cli-shared-utils#readme", "dependencies": { "chalk": "^2.3.0", + "cmd-shim": "^2.0.2", "joi": "^13.1.0", "ora": "^1.3.0", "readline": "^1.3.0", diff --git a/packages/@vue/cli-shared-utils/validate.js b/packages/@vue/cli-shared-utils/validate.js new file mode 100644 index 0000000000..b251500d00 --- /dev/null +++ b/packages/@vue/cli-shared-utils/validate.js @@ -0,0 +1,12 @@ +const joi = require('joi') + +// proxy to joi for option validation +exports.createSchema = fn => fn(joi) + +exports.validate = (obj, schema, options = {}) => { + joi.validate(obj, schema, options, err => { + if (err) { + throw err + } + }) +} diff --git a/packages/@vue/cli/lib/Creator.js b/packages/@vue/cli/lib/Creator.js index c006d84410..d4c79acdbe 100644 --- a/packages/@vue/cli/lib/Creator.js +++ b/packages/@vue/cli/lib/Creator.js @@ -104,7 +104,7 @@ module.exports = class Creator { const deps = Object.keys(options.plugins) if (isTestOrDebug) { // in development, avoid installation process - setupDevProject(context, deps) + await setupDevProject(context, deps) } else { await installDeps(context, packageManager, deps, cliOptions.registry) } diff --git a/packages/@vue/cli/lib/util/setupDevProject.js b/packages/@vue/cli/lib/util/setupDevProject.js index 2fafa7c8b6..760cc391a8 100644 --- a/packages/@vue/cli/lib/util/setupDevProject.js +++ b/packages/@vue/cli/lib/util/setupDevProject.js @@ -2,7 +2,7 @@ const fs = require('fs') const path = require('path') -const mkdirp = require('mkdirp') +const { linkBin } = require('@vue/cli-shared-utils') module.exports = function setupDevProject (targetDir, deps) { const pkg = require(path.resolve(targetDir, 'package.json')) @@ -19,11 +19,8 @@ module.exports = function setupDevProject (targetDir, deps) { path.resolve(targetDir, 'package.json'), JSON.stringify(pkg, null, 2) ) - const binPath = path.join(targetDir, 'node_modules', '.bin') - mkdirp.sync(binPath) - fs.symlinkSync( + return linkBin( require.resolve('@vue/cli-service/bin/vue-cli-service'), - path.join(binPath, 'vue-cli-service'), - 'junction' // needed for windows + path.join(targetDir, 'node_modules', '.bin', 'vue-cli-service') ) }