From c867c752d30c7961f9b2bdc0baba9994841664fa Mon Sep 17 00:00:00 2001 From: fangbinwei Date: Fri, 16 Oct 2020 03:24:21 +0800 Subject: [PATCH 1/3] fix(cli): fix module resolve of preset generator Fixes #5965 --- .../generator/index.js | 3 ++ .../generator/template/App.vue | 10 ++++ .../preset.json | 5 ++ packages/@vue/cli/__tests__/preset.spec.js | 46 +++++++++++++++++++ packages/@vue/cli/lib/GeneratorAPI.js | 24 +++++++--- 5 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/index.js create mode 100644 packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/template/App.vue create mode 100644 packages/@vue/cli/__tests__/mock-preset-with-template-extend/preset.json diff --git a/packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/index.js b/packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/index.js new file mode 100644 index 0000000000..43ae7a2fc3 --- /dev/null +++ b/packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/index.js @@ -0,0 +1,3 @@ +module.exports = (api, options) => { + api.render('./template', options) +} diff --git a/packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/template/App.vue b/packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/template/App.vue new file mode 100644 index 0000000000..7ca62db060 --- /dev/null +++ b/packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/template/App.vue @@ -0,0 +1,10 @@ +--- +extend: '@vue/cli-service/generator/template/src/App.vue' +replace: !!js/regexp / diff --git a/packages/@vue/cli/__tests__/mock-preset-with-template-extend/preset.json b/packages/@vue/cli/__tests__/mock-preset-with-template-extend/preset.json new file mode 100644 index 0000000000..63ec289edc --- /dev/null +++ b/packages/@vue/cli/__tests__/mock-preset-with-template-extend/preset.json @@ -0,0 +1,5 @@ +{ + "plugins": { + "@vue/cli-plugin-babel": {} + } +} diff --git a/packages/@vue/cli/__tests__/preset.spec.js b/packages/@vue/cli/__tests__/preset.spec.js index 30e4aae200..b393d7a416 100644 --- a/packages/@vue/cli/__tests__/preset.spec.js +++ b/packages/@vue/cli/__tests__/preset.spec.js @@ -2,6 +2,7 @@ jest.mock('inquirer') const { expectPrompts } = require('inquirer') const path = require('path') +const os = require('os') const fs = require('fs-extra') const create = require('@vue/cli/lib/create') @@ -57,6 +58,51 @@ test('should recognize generator/index.js in a local preset directory', async () expect(pkg.devDependencies).toHaveProperty('@vue/cli-plugin-babel') }) +test('should resolve module required by local preset generator', async () => { + const cwd = path.resolve(__dirname, '../../../test') + const name = 'test-preset-template-extend' + + await create( + name, + { + force: true, + git: false, + cwd, + preset: path.resolve(__dirname, './mock-preset-with-template-extend') + } + ) + + const testFile = await fs.readFile(path.resolve(cwd, name, 'App.vue'), 'utf-8') + expect(testFile).toMatch('Replace default script') + + const pkg = require(path.resolve(cwd, name, 'package.json')) + expect(pkg.devDependencies).toHaveProperty('@vue/cli-plugin-babel') +}) + +test('should resolve module required by local preset generator in the tmp folder ', async () => { + const cwd = path.resolve(__dirname, '../../../test') + const name = 'test-preset-template-extend-tmp' + + const tmpdir = path.resolve(os.tmpdir(), './mock-preset-with-template-extend') + await fs.copy(path.resolve(__dirname, './mock-preset-with-template-extend'), tmpdir) + + await create( + name, + { + force: true, + git: false, + cwd, + preset: tmpdir + } + ) + + const testFile = await fs.readFile(path.resolve(cwd, name, 'App.vue'), 'utf-8') + expect(testFile).toMatch('Replace default script') + + const pkg = require(path.resolve(cwd, name, 'package.json')) + expect(pkg.devDependencies).toHaveProperty('@vue/cli-plugin-babel') +}) + test('should recognize generator/index.js in a local preset directory by async generatory', async () => { const cwd = path.resolve(__dirname, '../../../test') const name = 'test-preset-template-async-generator' diff --git a/packages/@vue/cli/lib/GeneratorAPI.js b/packages/@vue/cli/lib/GeneratorAPI.js index 87e7987cec..e392923976 100644 --- a/packages/@vue/cli/lib/GeneratorAPI.js +++ b/packages/@vue/cli/lib/GeneratorAPI.js @@ -32,6 +32,11 @@ function pruneObject (obj) { return obj } +function isSubFile (folder, filename) { + const relative = path.relative(folder, filename) + return !!relative && !relative.startsWith('..') && !path.isAbsolute(relative) +} + class GeneratorAPI { /** * @param {string} id - Id of the owner plugin @@ -297,7 +302,7 @@ class GeneratorAPI { return filename }).join('/') const sourcePath = path.resolve(source, rawPath) - const content = renderFile(sourcePath, data, ejsOptions) + const content = renderFile(sourcePath, data, ejsOptions, this.generator.context) // only set file if it's not all whitespace, or is a Buffer (binary files) if (Buffer.isBuffer(content) || /[^\s]/.test(content)) { files[targetPath] = content @@ -309,7 +314,7 @@ class GeneratorAPI { const data = this._resolveData(additionalData) for (const targetPath in source) { const sourcePath = path.resolve(baseDir, source[targetPath]) - const content = renderFile(sourcePath, data, ejsOptions) + const content = renderFile(sourcePath, data, ejsOptions, this.generator.context) if (Buffer.isBuffer(content) || content.trim()) { files[targetPath] = content } @@ -474,7 +479,7 @@ function extractCallDir () { const replaceBlockRE = /<%# REPLACE %>([^]*?)<%# END_REPLACE %>/g -function renderFile (name, data, ejsOptions) { +function renderFile (name, data, ejsOptions, context) { if (isBinaryFileSync(name)) { return fs.readFileSync(name) // return buffer } @@ -510,9 +515,16 @@ function renderFile (name, data, ejsOptions) { } if (parsed.extend) { - const extendPath = path.isAbsolute(parsed.extend) - ? parsed.extend - : resolve.sync(parsed.extend, { basedir: path.dirname(name) }) + let extendPath = parsed.extend + if (!path.isAbsolute(parsed.extend)) { + try { + extendPath = resolve.sync(parsed.extend, { basedir: path.dirname(name) }) + } catch (e) { + if (isSubFile(context, name)) throw e + extendPath = resolve.sync(parsed.extend, { basedir: context }) + } + } + finalTemplate = fs.readFileSync(extendPath, 'utf-8') if (parsed.replace) { if (Array.isArray(parsed.replace)) { From 1da13ea449a51a2a4fa6308dc44ada288c470dcc Mon Sep 17 00:00:00 2001 From: fangbinwei Date: Sat, 17 Oct 2020 21:40:50 +0800 Subject: [PATCH 2/3] Revert "fix(cli): fix module resolve of preset generator" This reverts commit c867c752d30c7961f9b2bdc0baba9994841664fa. --- .../generator/index.js | 3 -- .../generator/template/App.vue | 10 ---- .../preset.json | 5 -- packages/@vue/cli/__tests__/preset.spec.js | 46 ------------------- packages/@vue/cli/lib/GeneratorAPI.js | 24 +++------- 5 files changed, 6 insertions(+), 82 deletions(-) delete mode 100644 packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/index.js delete mode 100644 packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/template/App.vue delete mode 100644 packages/@vue/cli/__tests__/mock-preset-with-template-extend/preset.json diff --git a/packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/index.js b/packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/index.js deleted file mode 100644 index 43ae7a2fc3..0000000000 --- a/packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/index.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = (api, options) => { - api.render('./template', options) -} diff --git a/packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/template/App.vue b/packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/template/App.vue deleted file mode 100644 index 7ca62db060..0000000000 --- a/packages/@vue/cli/__tests__/mock-preset-with-template-extend/generator/template/App.vue +++ /dev/null @@ -1,10 +0,0 @@ ---- -extend: '@vue/cli-service/generator/template/src/App.vue' -replace: !!js/regexp / diff --git a/packages/@vue/cli/__tests__/mock-preset-with-template-extend/preset.json b/packages/@vue/cli/__tests__/mock-preset-with-template-extend/preset.json deleted file mode 100644 index 63ec289edc..0000000000 --- a/packages/@vue/cli/__tests__/mock-preset-with-template-extend/preset.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "plugins": { - "@vue/cli-plugin-babel": {} - } -} diff --git a/packages/@vue/cli/__tests__/preset.spec.js b/packages/@vue/cli/__tests__/preset.spec.js index b393d7a416..30e4aae200 100644 --- a/packages/@vue/cli/__tests__/preset.spec.js +++ b/packages/@vue/cli/__tests__/preset.spec.js @@ -2,7 +2,6 @@ jest.mock('inquirer') const { expectPrompts } = require('inquirer') const path = require('path') -const os = require('os') const fs = require('fs-extra') const create = require('@vue/cli/lib/create') @@ -58,51 +57,6 @@ test('should recognize generator/index.js in a local preset directory', async () expect(pkg.devDependencies).toHaveProperty('@vue/cli-plugin-babel') }) -test('should resolve module required by local preset generator', async () => { - const cwd = path.resolve(__dirname, '../../../test') - const name = 'test-preset-template-extend' - - await create( - name, - { - force: true, - git: false, - cwd, - preset: path.resolve(__dirname, './mock-preset-with-template-extend') - } - ) - - const testFile = await fs.readFile(path.resolve(cwd, name, 'App.vue'), 'utf-8') - expect(testFile).toMatch('Replace default script') - - const pkg = require(path.resolve(cwd, name, 'package.json')) - expect(pkg.devDependencies).toHaveProperty('@vue/cli-plugin-babel') -}) - -test('should resolve module required by local preset generator in the tmp folder ', async () => { - const cwd = path.resolve(__dirname, '../../../test') - const name = 'test-preset-template-extend-tmp' - - const tmpdir = path.resolve(os.tmpdir(), './mock-preset-with-template-extend') - await fs.copy(path.resolve(__dirname, './mock-preset-with-template-extend'), tmpdir) - - await create( - name, - { - force: true, - git: false, - cwd, - preset: tmpdir - } - ) - - const testFile = await fs.readFile(path.resolve(cwd, name, 'App.vue'), 'utf-8') - expect(testFile).toMatch('Replace default script') - - const pkg = require(path.resolve(cwd, name, 'package.json')) - expect(pkg.devDependencies).toHaveProperty('@vue/cli-plugin-babel') -}) - test('should recognize generator/index.js in a local preset directory by async generatory', async () => { const cwd = path.resolve(__dirname, '../../../test') const name = 'test-preset-template-async-generator' diff --git a/packages/@vue/cli/lib/GeneratorAPI.js b/packages/@vue/cli/lib/GeneratorAPI.js index e392923976..87e7987cec 100644 --- a/packages/@vue/cli/lib/GeneratorAPI.js +++ b/packages/@vue/cli/lib/GeneratorAPI.js @@ -32,11 +32,6 @@ function pruneObject (obj) { return obj } -function isSubFile (folder, filename) { - const relative = path.relative(folder, filename) - return !!relative && !relative.startsWith('..') && !path.isAbsolute(relative) -} - class GeneratorAPI { /** * @param {string} id - Id of the owner plugin @@ -302,7 +297,7 @@ class GeneratorAPI { return filename }).join('/') const sourcePath = path.resolve(source, rawPath) - const content = renderFile(sourcePath, data, ejsOptions, this.generator.context) + const content = renderFile(sourcePath, data, ejsOptions) // only set file if it's not all whitespace, or is a Buffer (binary files) if (Buffer.isBuffer(content) || /[^\s]/.test(content)) { files[targetPath] = content @@ -314,7 +309,7 @@ class GeneratorAPI { const data = this._resolveData(additionalData) for (const targetPath in source) { const sourcePath = path.resolve(baseDir, source[targetPath]) - const content = renderFile(sourcePath, data, ejsOptions, this.generator.context) + const content = renderFile(sourcePath, data, ejsOptions) if (Buffer.isBuffer(content) || content.trim()) { files[targetPath] = content } @@ -479,7 +474,7 @@ function extractCallDir () { const replaceBlockRE = /<%# REPLACE %>([^]*?)<%# END_REPLACE %>/g -function renderFile (name, data, ejsOptions, context) { +function renderFile (name, data, ejsOptions) { if (isBinaryFileSync(name)) { return fs.readFileSync(name) // return buffer } @@ -515,16 +510,9 @@ function renderFile (name, data, ejsOptions, context) { } if (parsed.extend) { - let extendPath = parsed.extend - if (!path.isAbsolute(parsed.extend)) { - try { - extendPath = resolve.sync(parsed.extend, { basedir: path.dirname(name) }) - } catch (e) { - if (isSubFile(context, name)) throw e - extendPath = resolve.sync(parsed.extend, { basedir: context }) - } - } - + const extendPath = path.isAbsolute(parsed.extend) + ? parsed.extend + : resolve.sync(parsed.extend, { basedir: path.dirname(name) }) finalTemplate = fs.readFileSync(extendPath, 'utf-8') if (parsed.replace) { if (Array.isArray(parsed.replace)) { From ff5184e9fc32e0691ddc991f0b11e774491da0a3 Mon Sep 17 00:00:00 2001 From: fangbinwei Date: Sat, 17 Oct 2020 21:44:31 +0800 Subject: [PATCH 3/3] fix(cli): install dependencies if preset contains generator and package.json Fixes #5965 --- packages/@vue/cli/lib/Creator.js | 6 ++++++ packages/@vue/cli/lib/util/loadPresetFromDir.js | 2 ++ 2 files changed, 8 insertions(+) diff --git a/packages/@vue/cli/lib/Creator.js b/packages/@vue/cli/lib/Creator.js index 1cbe1c1219..b107a70f37 100644 --- a/packages/@vue/cli/lib/Creator.js +++ b/packages/@vue/cli/lib/Creator.js @@ -136,6 +136,7 @@ module.exports = class Creator extends EventEmitter { await clearConsole() const pm = new PackageManager({ context, forcePackageManager: packageManager }) + let presetPm = null log(`✨ Creating project in ${chalk.yellow(context)}.`) this.emit('creation', { event: 'creating' }) @@ -154,6 +155,10 @@ module.exports = class Creator extends EventEmitter { const deps = Object.keys(preset.plugins) deps.forEach(dep => { if (preset.plugins[dep]._isPreset) { + const { _hasPackageJson, _dir } = preset.plugins[dep] + if (!_hasPackageJson) return + if (!path.relative(pm.context, _dir)) return + presetPm = new PackageManager({ context: _dir }) return } @@ -213,6 +218,7 @@ module.exports = class Creator extends EventEmitter { // in development, avoid installation process await require('./util/setupDevProject')(context) } else { + presetPm && await presetPm.install() await pm.install() } diff --git a/packages/@vue/cli/lib/util/loadPresetFromDir.js b/packages/@vue/cli/lib/util/loadPresetFromDir.js index 096368b2ce..a9ec499f93 100644 --- a/packages/@vue/cli/lib/util/loadPresetFromDir.js +++ b/packages/@vue/cli/lib/util/loadPresetFromDir.js @@ -14,6 +14,8 @@ module.exports = async function loadPresetFromDir (dir) { if (hasGenerator) { (preset.plugins || (preset.plugins = {}))[dir.replace(/[\/]$/, '')] = { _isPreset: true, + _dir: dir, + _hasPackageJson: fs.existsSync(path.join(dir, 'package.json')), prompts: true } }