One, foreword

Due to the recent need to make a vueCodeBase for internal use, The normal vue init template <project-name> scaffold can not meet the need, so I hope to be able to implement a custom scaffolding tool based on template template. This article on VUE-CLI command source code to do a simple analysis, The purpose is to understand the scaffolding generation process and facilitate custom scaffolding vue-cli 2.9.6 source code: https://github.com/vuejs/vue-cli/tree/v2 vue official template source: https://github.com/vuejs-templates vuejs - templates (webpack) document:  https://vuejs-templates.github.io/webpack/Copy the code

Two, vuE-CLI use and source download

NPM install VUE – CLI:

npm install -g vue-cli
Copy the code

Create the project using the vue init command (using the Webpack template)

vue init webpack <project-name>
Copy the code

With these two commands, you get a scaffold for your project based on the WebPack template


Clone Vue – CLI Branch V2:

git clone -b v2 https://github.com/vuejs/vue-cli
Copy the code

Vue-cli source code project structure:


Vue-cli command analysis

Note the following output after the installation is successful:



View/usr/local/lib/node_modules/vue – cli/bin:

To view the package.json file:

Bin Specifies the location of the command executable file in vue-cli 2.X, vue-build and vue-create are not supported


Vue command:

Bin /vue

#! /usr/bin/env node

const program = require('commander')

program
  .version(require('.. /package').version)
  .usage('<command> [options]')
  .command('init'.'generate a new project from a template')
  .command('list'.'list available official templates')
  .command('build'.'prototype a new project')
  .command('create'.'(for v3 warning only)')

program.parse(process.argv)
Copy the code

Run the vue command to view the output:


2, vue-init:

Vue init webpack vueCodeBase:

Note: interactive command fetching parameters is not implemented in VUe-CLI, but in the template project mate.js, which mainly describes the cli command source code, namely vue init implementationCopy the code

Vue – init source code:

#! /usr/bin/env node

// Download code from repository -GitHub, GitLab, Bitbucket
const download = require('download-git-repo')
// Create a subcommand, cut command line arguments and execute it
const program = require('commander')
// Check whether the file exists
const exists = require('fs').existsSync
// Path module
const path = require('path')
// loading
const ora = require('ora')
// Get the home directory path
const home = require('user-home')
// Absolute path is converted to relative path
const tildify = require('tildify')
// Command line font color
const chalk = require('chalk')
// Interactive command line, you can ask questions at the console
const inquirer = require('inquirer')
// Wrap the rm -rf command to delete files and folders
const rm = require('rimraf').sync
/ / log
const logger = require('.. /lib/logger')
// Automatically generated
const generate = require('.. /lib/generate')
// Check the version
const checkVersion = require('.. /lib/check-version')
/ / warning
const warnings = require('.. /lib/warnings')
const localPath = require('.. /lib/local-path')

// Whether the method is local
const isLocalPath = localPath.isLocalPath
// Template path method
const getTemplatePath = localPath.getTemplatePath

Args [0] Template type * program.args[1] Custom project name * program.clone clone * program.offline Offline */
program
  .usage('<template-name> [project-name]')
  .option('-c, --clone'.'use git clone')
  .option('--offline'.'use cached template')

/** * Help. */
program.on('--help'.() = > {
  console.log(' Examples:')
  console.log()
  console.log(chalk.gray(' # create a new project with an official template'))
  console.log(' $ vue init webpack my-project')
  console.log()
  console.log(chalk.gray(' # create a new project straight from a github template'))
  console.log(' $ vue init username/repo my-project')
  console.log()
})

/** * Help. */
function help () {
  program.parse(process.argv)
  if (program.args.length < 1) return program.help()
}
help()

/** * Settings. */
// Template type: get the first argument, such as webpack
let template = program.args[0]
// Is there a "/" sign
const hasSlash = template.indexOf('/') > -1
// Customize the project name, such as my-project
const rawName = program.args[1]

// rawName exists or is ". Is considered to be built under the current directory
constinPlace = ! rawName || rawName ==='. '
// path.relative () : Returns relative paths based on the current working directory
const name = inPlace ? path.relative('.. / ', process.cwd()) : rawName
// Merge paths
const to = path.resolve(rawName || '. ')
// Check whether the parameter is clone
const clone = program.clone || false
// path.join () : Join all the given paths together using platform-specific separators, then normalize the resulting path
/ / such as/Users/admin /. Vue - templates/webpack
const tmp = path.join(home, '.vue-templates', template.replace(/[\/:]/g.The '-'))
// Offline or not: If it is offline, use the current address directly, otherwise go to the online repository to download
if (program.offline) {
  console.log(`> Use cached template at ${chalk.yellow(tildify(tmp))}`)
  // Set it to an offline address
  template = tmp
}

/** * Padding. */
console.log()
process.on('exit'.() = > {
  console.log()
})

// If the directory exists, run is executed, otherwise run is executed directly
if (inPlace || exists(to)) {
  inquirer.prompt([{
    type: 'confirm'.message: inPlace
      // Whether to build the project under the current directory
      ? 'Generate project in current directory? '
      // The build directory already exists
      : 'Target directory exists. Continue? '.name: 'ok'
  }]).then(answers= > {
    if (answers.ok) {
      run()
    }
  }).catch(logger.fatal)
} else {
  run()
}

/** * Check, download and generate the project. */
function run () {
  // Local template
  if (isLocalPath(template)) {
  	// Get the absolute path
    const templatePath = getTemplatePath(template)
    // Exist - generated using a local template
    if (exists(templatePath)) {
      generate(name, templatePath, to, err= > {
        if (err) logger.fatal(err)
        console.log()
        logger.success('Generated "%s".', name)
      })
    // Local template does not exist - error reported
    } else {
      logger.fatal('Local template "%s" not found.', template)
    }
  // Non-local template
  } else {
    // Version check
    checkVersion(() = > {
      // Do not contain "/", go to the official website to download
      if(! hasSlash) {// use official templates
        const officialTemplate = 'vuejs-templates/' + template
        // Template name with "#"
        if (template.indexOf(The '#')! = = -1) {
          downloadAndGenerate(officialTemplate)
        } else {
          if (template.indexOf('2.0')! = = -1) {
            warnings.v2SuffixTemplatesDeprecated(template, inPlace ? ' ' : name)
            return
          }
          // warnings.v2BranchIsNowDefault(template, inPlace ? '' : name)
          // Download the official template
          downloadAndGenerate(officialTemplate)
        }
      // Include "/", go to your repository to download
      } else {
        downloadAndGenerate(template)
      }
    })
  }
}

/** * generate a template repo@param {String} template* /
function downloadAndGenerate (template) {
  // loading
  const spinner = ora('downloading template')
  spinner.start()
  
  // Remove if local template exists
  if (exists(tmp)) rm(tmp)
  
  Git-repo: download the code from GitHub, GitLab, Bitbucket
  // template: name of the template TMP: path of the template. Clone: Whether to use the git clone template. Err: error information
  download(template, tmp, { clone }, err= > {
    spinner.stop()
    // error!!
    if (err) logger.fatal('Failed to download repo ' + template + ':' + err.message.trim())
    generate(name, tmp, to, err= > {
      // error!!
      if (err) logger.fatal(err)
      console.log()
      // success
      logger.success('Generated "%s".', name)
    })
  })
}
Copy the code

Vue init ‘template-name’ ‘project-name’ Indicates the execution process

1. Obtain parameters

Args [0] Template type * program.args[1] Custom project name * program.clone clone * program.offline Offline */
program
  .usage('<template-name> [project-name]')
  .option('-c, --clone'.'use git clone')
  .option('--offline'.'use cached template')
Copy the code

2. Obtain template path:

// rawName exists or is ". Is considered to be built under the current directory
constinPlace = ! rawName || rawName ==='. '
// path.relative () : Returns relative paths based on the current working directory
const name = inPlace ? path.relative('.. / ', process.cwd()) : rawName
// Merge paths
const to = path.resolve(rawName || '. ')
// Check whether the parameter is clone
const clone = program.clone || false
// path.join () : Join all the given paths together using platform-specific separators, then normalize the resulting path
/ / such as/Users/admin /. Vue - templates/webpack
const tmp = path.join(home, '.vue-templates', template.replace(/[\/:]/g.The '-'))
Copy the code

3, run function: distinguish local and offline, download template

  // The local path exists
  if (isLocalPath(template)) {
  	// Get the absolute path
    const templatePath = getTemplatePath(template)
    // Local download...
  } else {
  	   // Do not contain "/", go to the official website to download
      if(! hasSlash) {const officialTemplate = 'vuejs-templates/' + template
      // Include "/", go to your repository to download
      } else{}}Copy the code

Reference to open source projects:

Commander: command line tools development libraries https://github.com/tj/commander.js/ the generate command invocation lib/generate file, use the metalsmith: control output content https://github.com/segmentio/metalsmithCopy the code

Vue -list command:

Vue-list is a simple way to get and display a list of templates in the official Git repository

Vue – list the source code:

#! /usr/bin/env node

const logger = require('.. /lib/logger')
const request = require('request')
const chalk = require('chalk')

/** * Padding. */
console.log()
process.on('exit'.() = > {
  console.log()
})

/ * * * the List repos. * warehouse List: https://api.github.com/users/vuejs-templates/repos * /
request({
  url: 'https://api.github.com/users/vuejs-templates/repos'.headers: {
    'User-Agent': 'vue-cli'}},(err, res, body) = > {
  if (err) logger.fatal(err)
  const requestBody = JSON.parse(body)
  if (Array.isArray(requestBody)) {
    console.log(' Available official templates:')
    console.log()
    requestBody.forEach(repo= > {
      console.log(
        // Yellow star symbol
        ' ' + chalk.yellow('★') +
        // Use blue font for the warehouse name
        ' ' + chalk.blue(repo.name) +
        The '-' + repo.description)
    })
  } else {
    console.error(requestBody.message)
  }
})
Copy the code

####4, vue-build command

The vue-build command is not supported in vue-cli 2.x:

Vue – build the source code:

#! /usr/bin/env node

const chalk = require('chalk')

console.log(chalk.yellow(
  '\n' +
  ' We are slimming down vue-cli to optimize the initial installation by ' +
  'removing the `vue build` command.\n' +
  '  Check out Poi (https://github.com/egoist/poi) which offers the same functionality!' +
  '\n'
))
Copy the code

Translation:

We reduced vuE-CLI by removing the "vue build" command to optimize the initial installation. Check out the Poi (https://github.com/egoist/poi), provide the same functionality.Copy the code

Poi:

Front-end engineering solutionsCopy the code

5, vue – create command:

vue createisVue CLI 3The command

Uninstall vue-cli, install @vue/cli, and upgrade to Vue CLI 3

Vue – create the source code:

#! /usr/bin/env node

const chalk = require('chalk')

console.log()
console.log(
  ` ` +
  chalk.yellow(`vue create`) +
  ' is a Vue CLI 3 only command and you are using Vue CLI ' +
  require('.. /package.json').version + '. '
)
console.log(` You may want to run the following to upgrade to Vue CLI 3:`)
console.log()
console.log(chalk.cyan(` npm uninstall -g vue-cli`))
console.log(chalk.cyan(` npm install -g @vue/cli`))
console.log()
Copy the code

Download the git-repo template

Through the analysis of vuE-init command execution process, understand the role of various parameters of the command can be used to specify online/offline command, specify warehouse to obtain VUE scaffolding template

Generate (name, TMP, to, err => {})}Copy the code

Download template usedThe download git – repo:

Github.com/flipxfx/dow…

Key code:

Github.com/flipxfx/dow…

Core logic:

1. On vue-CLI, run the download-git-repo method to download the template from the template repository:
/**
 * Download `repo` to `dest` and callback `fn(err)`.
 *
 * @param {String} Repo warehouse *@param {String} Dest target *@param {Object} * opts parameters@param {Function} Fn callback * /
function download (repo, dest, opts, fn) {
  / / callback
  if (typeof opts === 'function') {
    fn = opts
    opts = null
  }
  // clone?
  opts = opts || {}
  var clone = opts.clone || false

	// Standard warehouse string (convert to github.com, gitlab.com, bitbucket.com according to type)
  repo = normalize(repo)
  Github, Gitlab, bitbucket
  var url = repo.url || getUrl(repo, clone)

  // clone
  if (clone) {
  	Var gitclone = require('git-clone')
    gitclone(url, dest, { checkout: repo.checkout, shallow: repo.checkout === 'master' }, function (err) {
      if (err === undefined) {
        rm(dest + '/.git')
        fn()
      } else {
        fn(err)
      }
    })
  Var downloadUrl = require('download')
  } else {
    downloadUrl(url, dest, { extract: true.strip: 1.mode: '666'.headers: { accept: 'application/zip' } })
      .then(function (data) {
        fn()
      })
      .catch(function (err) {
        fn(err)
      })
  }
}
Copy the code

Vue-cli calls this download method and eventually executes gitClone or downloadUrl to download the code

But before that, a series of processing is done to the warehouse address:


2, specification warehouse string (Convert to github.com based on type.gitlab.com.bitbucket.com)

The warehouse address repo of type string is processed into a REPO object

/** * Normalize a repo string. * @param {string} repo string * @return {Object} returns the repo address Object */ function Normalize (repo) {// Direct matches var regex = /^(? :(direct):([^#]+)(? : # (. +))? $/ var match = regex.exec(repo) if (match) { var url = match[2] var checkout = match[3] || 'master' return { type: 'direct', url: url, checkout: checkout}} else {// Other type match regex = /^(? :(github|gitlab|bitbucket):)? (? 1: (+) :)? \ [^ \ /] +)/([^ #] +) (? : # (. +))? $/ match = regex.exec(repo) var type = match[1] || 'github' var origin = match[2] || null var owner = match[3] var name = match [4] var checkout = match [5] | | 'master' / / if the origin is empty, If (origin == null) {if (type === 'github') origin = 'github.com' else if (type === 'gitlab') origin = 'gitlab.com' else if (type === 'bitbucket') origin = 'bitbucket.com'} Type, // warehouse type origin: origin, // warehouse host owner: owner, // warehouse owner name: name, // project name: checkout // branch}}Copy the code

Download-git-repo Specifies the download rules

Documents: www.npmjs.com/package/dow… Js regular testing tools: tools.jb51.net/regex/javas…

Download examples:

download('gitlab:mygitlab.com:flipxfx/download-git-repo-fixture#my-branch', 'test/tmp', function (err) {
  console.log(err ? 'Error' : 'Success')
})
Copy the code

Shorthand

^ (? :(github|gitlab|bitbucket):)? (? 1: (+) :)? \ [^ \ /] +)/([^ #] +) (? : # (. +))? $ flipxfx/download-git-repo-fixture bitbucket:flipxfx/download-git-repo-fixture#my-branch gitlab:mygitlab.com:flipxfx/download-git-repo-fixture#my-branchCopy the code

direct

^ (? :(direct):([^#]+)(? : # (. +))? $ direct:https://gitlab.com/flipxfx/download-git-repo-fixture/repository/archive.zip direct:https://gitlab.com/flipxfx/download-git-repo-fixture.git direct:https://gitlab.com/flipxfx/download-git-repo-fixture.git#my-branchCopy the code

3, build download template URL address (support github, Gitlab, bitbucket) :

Using the REPO repository object converted from the previous step, further transformation results in the URL

/** * Return a zip or git url for a given 'repo'@param {Object} Repo repository object *@return {String}		url* /
function getUrl (repo, clone) {
  var url

  // Get the source code using the protocol and add trailing slashes or colons (for SSH)
  // Attach protocol (attach git@ or https:// protocol) :
  var origin = addProtocol(repo.origin, clone)
  if (/^git\@/i.test(origin))
    origin = origin + ':'
  else
    origin = origin + '/'

  / / build the URL
  // clone
  if (clone) {
    url = origin + repo.owner + '/' + repo.name + '.git'
  // Non-clone (github, Gitlab, Bitbucket)
  } else {
  	// github
    if (repo.type === 'github')
      url = origin + repo.owner + '/' + repo.name + '/archive/' + repo.checkout + '.zip'
    // gitlab
    else if (repo.type === 'gitlab')
      url = origin + repo.owner + '/' + repo.name + '/repository/archive.zip? ref=' + repo.checkout
    // bitbucket
    else if (repo.type === 'bitbucket')
      url = origin + repo.owner + '/' + repo.name + '/get/' + repo.checkout + '.zip'
  }

  return url
}
Copy the code

Before constructing the URL, add an additional protocol to your Git repository (attach the git@ or https:// protocol) :

/** * Adds protocol to URL in none specified *@param {String} url	
 * @return {String}* /
function addProtocol (origin, clone) {
  if (!/^(f|ht)tps? :\/\//i.test(origin)) {
    if (clone)
      origin = 'git@' + origin
    else
      origin = 'https://' + origin
  }

  return origin
}
Copy the code

Four, render template

The template has been downloaded and rendered

Generate (name, TMP, to, err => {})}Copy the code

When downloading the template, we get the parameters we entered in interactive command mode

Configure the template based on the parameters you set, referencing… /lib/generate

The generate. Js file:

Github.com/vuejs/vue-c…

Statement:

// Highlight the printed information
const chalk = require('chalk')
// Static web site builder
const Metalsmith = require('metalsmith')
// Handlebars template engine
const Handlebars = require('handlebars')
// Asynchronous processing tool
const async = require('async')
// The template engine renders
const render = require('consolidate').handlebars.render
// node path module
const path = require('path')
// Multi-condition matching
const multimatch = require('multimatch')
// Get the template configuration
const getOptions = require('./options')
// Ask the developer
const ask = require('./ask')
// File filtering
const filter = require('./filter')
/ / log
const logger = require('./logger')
Copy the code

Main logic:

/** * Generate a template given a 'SRC' and 'dest'. * Generate a template given a 'SRC' and 'dest' *@param {String} name
 * @param {String} src
 * @param {String} dest
 * @param {Function} done* /
module.exports = function generate (name, src, dest, done) {
  -src is the temporary path after the template is downloaded successfully
  const opts = getOptions(name, src)
  // Initialize the Metalsmith object - read the tempalte directory of the template
  // Metalsmith returns a mapping object for the file path and content to be processed by metalsmith middleware
  const metalsmith = Metalsmith(path.join(src, 'template'))
  // Add a variable to metalsmith
  const data = Object.assign(metalsmith.metadata(), {
    destDirName: name,
    inPlace: dest === process.cwd(),
    noEscape: true
  })
  
  // Register the helper in the configuration object
  opts.helpers && Object.keys(opts.helpers).map(key= > {
    Handlebars.registerHelper(key, opts.helpers[key])
  })
  const helpers = { chalk, logger }

  // Configures whether the object contains a before function and executes it if it does
  if (opts.metalsmith && typeof opts.metalsmith.before === 'function') {
    opts.metalsmith.before(metalsmith, opts, helpers)
  }

  Vue CLI uses three middleware to process templates
  metalsmith
    Prompts. // Ask the prompts in mate.js
	.use(askQuestions(opts.prompts))
	// Filter files according to the configuration
	.use(filterFiles(opts.filters))
	// Render the template file
	.use(renderTemplateFiles(opts.skipInterpolation))

  // Configures whether the object contains the after function and executes it if it does
  if (typeof opts.metalsmith === 'function') {
    opts.metalsmith(metalsmith, opts, helpers)
  } else if (opts.metalsmith && typeof opts.metalsmith.after === 'function') {
    opts.metalsmith.after(metalsmith, opts, helpers)
  }

  metalsmith.clean(false)
    .source('. ') // Start from the template root instead of "./Src ", which is MalalSmith's default "source"
    .destination(dest)
    .build((err, files) = > {
      done(err)
      // // the configuration object is executed if it has complete functions
      if (typeof opts.complete === 'function') {
        const helpers = { chalk, logger, files }
        opts.complete(data, helpers)
      } else {
        // The configuration object has completeMessage, which executes the logMessage function
        logMessage(opts.completeMessage, data)
      }
    })

  return data
}
Copy the code

Note:

What is read is the tempalte directory of the template

Const metalsmith = metalsmith (path.join(SRC, ‘template’))


Register the Handlebars template helper-if_eq and unless_eq

Handlebars.registerHelper('if_eq'.function (a, b, opts) {
  return a === b
    ? opts.fn(this)
    : opts.inverse(this)
})

Handlebars.registerHelper('unless_eq'.function (a, b, opts) {
  return a === b
    ? opts.inverse(this)
    : opts.fn(this)})Copy the code

The middleware askQuestions is used to read user input

Prompt prompts. /** * Create a middleware for asking questions@param {Object} prompts
 * @return {Function}* /
function askQuestions (prompts) {
  return (files, metalsmith, done) = > {
    ask(prompts, metalsmith.metadata(), done)
  }
}
Copy the code

Meta. {js, json} the sample:

{ "prompts": { "name": { "type": "string", "required": true, "message" : "Project name" }, "version": { "type": "Input", "message" : "the project 's version", "default" : "1.0.0"}}}Copy the code

In ask, users are conditionally consulted for prompts in meta information

// vue-cli/lib/ask.js#prompt
inquirer.prompt([{
	type: prompt.type,
	message: prompt.message,
	default: prompt.default
	/ /...}].function(answers) {
	// Save the user's input
})
Copy the code

After being processed by askQuestions middleware, global metadata is an object with prompt key as key and user input as value:

{name: 'my-project', version: '1.0.0'... }Copy the code

Middleware filterFiles- Filters based on the meta information in the filters file:

/** * Create a middleware for filtering files@param {Object} filters
 * @return {Function}* /
function filterFiles (filters) {
  return (files, metalsmith, done) = > {
    filter(files, filters, metalsmith.metadata(), done)
  }
}
Copy the code

The filter source code:

// vue-cli/lib/filter.js
module.exports = function (files, filters, data, done) {
  // Call done() without filters
  if(! filters) {return done()
  }
  
  // Get all file names
  var fileNames = Object.keys(files)
 	
  // Walk through the filters to match and delete unwanted files
  Object.keys(filters).forEach(function (glob) {
    fileNames.forEach(function (file) {
      if (match(file, glob, { dot: true{}))// Get the matching value
        var condition = filters[glob]
        // evaluate is used to execute js expressions in vue-cli/lib/eval.js
        // var fn = new Function('data', 'with (data) { return ' + exp + '}')
        if(! evaluate(condition, data)) {// Delete files - Filters out unwanted files based on user input
          delete files[file]
        }
      }
    })
  })
  done()
}
Copy the code

Render templates with renderTemplateFiles middleware:

/** * Template in place plugin@param {Object} Files All file objects *@param {Metalsmith} Metalsmith Metalsmith object *@param {Function} done* /
function renderTemplateFiles (skipInterpolation) {

  // skipInterpolation if it is not an array, convert it to an array and make sure it is an array
  skipInterpolation = typeof skipInterpolation === 'string'
    ? [skipInterpolation]
    : skipInterpolation   
     
  return (files, metalsmith, done) = > {
    // Get all keys of the files object
    const keys = Object.keys(files) 
    // Get the metadata of the metalsmith object
    const metalsmithMetadata = metalsmith.metadata() 
    // Asynchronously process all files corresponding to keys
    async.each(keys, (file, next) = > {  
      // Skip file with skipInterpolation configuration
      if (skipInterpolation && multimatch([file], skipInterpolation, { dot: true }).length) {
        return next()
      }
      
      // Get the file contents
      const str = files[file].contents.toString()
      
      // Skip files that don't fit handlebars syntax (don't render files without mustaches)
      if (!/{{([^{}]+)}}/g.test(str)) {  
        return next()
      }
      
      // Call handlebars to complete the file rendering
      render(str, metalsmithMetadata, (err, res) = > {
        
        if (err) {
          err.message = ` [${file}] ${err.message}`
          return next(err)
        }
        
        files[file].contents = new Buffer(res)
        next()
      })
    }, done)
  }
}
Copy the code

Display template completion information:

After the template file is rendered, Metalsmith will build the final result into the dest directory. If the build fails, it will pass the ERR message to the callback output. If the meta information is complete, the function will be called. If the meta information is completeMessage, the function will be printed:

/**
 * Display template complete message.
 *
 * @param {String} Message message *@param {Object} The data data * /
function logMessage (message, data) {
  // If there is no message, return
  if(! message)return  
  
  // Render information
  render(message, data, (err, res) = > {
    if (err) {
      console.error('\n Error when rendering template complete message: ' + err.message.trim())  
    } else {
      console.log('\n' + res.split(/\r? \n/g).map(line= > ' ' + line).join('\n'))}}}Copy the code

Read configuration information from meta(.json/.js) and getGitUser in options.js

const getGitUser = require('./git-user')

module.exports = function options (name, dir) {
  // Read the meta(.json/.js) information of the template
  // dir is the temporary path after the template is downloaded successfully
  const opts = getMetadata(dir)
  
  // Add field defaults to the configuration object
  setDefault(opts, 'name', name)
  
  // Check whether the name field in the configuration object is valid
  setValidateName(opts)
  
  // Read the user's git nickname and mailbox to set the default properties of meta information
  const author = getGitUser()
  if (author) {
    setDefault(opts, 'author', author)
  }

  return opts
}
Copy the code

To summarize the template rendering process:

1, get template configuration, 2, initialize Metalsmith, add variables to Metalsmith 3, Handlebars template registration helper 4, execute before function (if any) 5, ask questions, filter files, render template files 6, Execute after (if any) 7, build the project, 8, after the build is complete, print the completeMessage message in the configuration object, if there is an error, execute the callback done(err).Copy the code

Other files in the lib folder are also used, briefly explained:

Options. js gets the template configuration file to set the name field and checks if name is valid to set authorgetMetadata: Gets configuration information from meta. Js or meta. JsonsetDefault: Adds default field values to the configuration objectsetValidateNameGit-user. js is used to get the username and email of the local Git configuration and return a string in the form of name < mailbox >.evalPrompts. Js to resolve the Meta-js or meta-json field as a question and ask file.js to remove the unwanted template file according to metalsmith.metadata() local-path.jsisLocalPath: UNIX (with ". or"/"WINDOWS(start with something like "C:")getTemplatePath: Check -version.js to check whether the local node version meets the requirements specified in package.json. And package. The json version field in comparison, prompt upgrade warnings. Js v2SuffixTemplatesDeprecated: prompt "-2.0"Template deprecated, official default2.0. You don't need to use "-"2.0Distinguish between"1.0and2.0V2BranchIsNowDefault: Vue-init is annotated and is used by default2.0
logger.js
Copy the code

Fifth, obtain vue scaffold templates from the unofficial template library to generate scaffold projects

From vue init to template download and scaffolding generation, you can use the vue init command to retrieve custom vue scaffolding templates from a repository

For example, we can fork an official WebPack scaffold template to our Github repository

Official Webpack template address:Github.com/vuejs-templ…



Then, vue scaffolding is generated with vue init username/repo my-project

For example: vue init BraveWangDev/webpack my-project


Six, the end

By analyzing the command source code of Vue CLI, we understand the operation mechanism of the command provided by Vue

For custom Vue scaffold templates, vue-init, generate.js, options.js, ask.js, filter.js, these five files form the main flow of vue-CLI build projects

The template project obtained through vue-CLI must have the following two points: 1, the root directory of the project must have the meta.js file 2, the project must have the template folder, which is the real scaffold template

Now it’s time to customize the Vue scaffolding template