You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					213 lines
				
				5.7 KiB
			
		
		
			
		
	
	
					213 lines
				
				5.7 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								const path = require('path')
							 | 
						||
| 
								 | 
							
								const hash = require('hash-sum')
							 | 
						||
| 
								 | 
							
								const { semver, matchesPluginId } = require('@vue/cli-shared-utils')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Note: if a plugin-registered command needs to run in a specific default mode,
							 | 
						||
| 
								 | 
							
								// the plugin needs to expose it via `module.exports.defaultModes` in the form
							 | 
						||
| 
								 | 
							
								// of { [commandName]: mode }. This is because the command mode needs to be
							 | 
						||
| 
								 | 
							
								// known and applied before loading user options / applying plugins.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class PluginAPI {
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * @param {string} id - Id of the plugin.
							 | 
						||
| 
								 | 
							
								   * @param {Service} service - A vue-cli-service instance.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  constructor (id, service) {
							 | 
						||
| 
								 | 
							
								    this.id = id
							 | 
						||
| 
								 | 
							
								    this.service = service
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  get version () {
							 | 
						||
| 
								 | 
							
								    return require('../package.json').version
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  assertVersion (range) {
							 | 
						||
| 
								 | 
							
								    if (typeof range === 'number') {
							 | 
						||
| 
								 | 
							
								      if (!Number.isInteger(range)) {
							 | 
						||
| 
								 | 
							
								        throw new Error('Expected string or integer value.')
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      range = `^${range}.0.0-0`
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (typeof range !== 'string') {
							 | 
						||
| 
								 | 
							
								      throw new Error('Expected string or integer value.')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (semver.satisfies(this.version, range, { includePrerelease: true })) return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    throw new Error(
							 | 
						||
| 
								 | 
							
								      `Require @vue/cli-service "${range}", but was loaded with "${this.version}".`
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Current working directory.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  getCwd () {
							 | 
						||
| 
								 | 
							
								    return this.service.context
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Resolve path for a project.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param {string} _path - Relative path from project root
							 | 
						||
| 
								 | 
							
								   * @return {string} The resolved absolute path.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  resolve (_path) {
							 | 
						||
| 
								 | 
							
								    return path.resolve(this.service.context, _path)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Check if the project has a given plugin.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param {string} id - Plugin id, can omit the (@vue/|vue-|@scope/vue)-cli-plugin- prefix
							 | 
						||
| 
								 | 
							
								   * @return {boolean}
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  hasPlugin (id) {
							 | 
						||
| 
								 | 
							
								    return this.service.plugins.some(p => matchesPluginId(id, p.id))
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Register a command that will become available as `vue-cli-service [name]`.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param {string} name
							 | 
						||
| 
								 | 
							
								   * @param {object} [opts]
							 | 
						||
| 
								 | 
							
								   *   {
							 | 
						||
| 
								 | 
							
								   *     description: string,
							 | 
						||
| 
								 | 
							
								   *     usage: string,
							 | 
						||
| 
								 | 
							
								   *     options: { [string]: string }
							 | 
						||
| 
								 | 
							
								   *   }
							 | 
						||
| 
								 | 
							
								   * @param {function} fn
							 | 
						||
| 
								 | 
							
								   *   (args: { [string]: string }, rawArgs: string[]) => ?Promise
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  registerCommand (name, opts, fn) {
							 | 
						||
| 
								 | 
							
								    if (typeof opts === 'function') {
							 | 
						||
| 
								 | 
							
								      fn = opts
							 | 
						||
| 
								 | 
							
								      opts = null
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    this.service.commands[name] = { fn, opts: opts || {}}
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Register a function that will receive a chainable webpack config
							 | 
						||
| 
								 | 
							
								   * the function is lazy and won't be called until `resolveWebpackConfig` is
							 | 
						||
| 
								 | 
							
								   * called
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param {function} fn
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  chainWebpack (fn) {
							 | 
						||
| 
								 | 
							
								    this.service.webpackChainFns.push(fn)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Register
							 | 
						||
| 
								 | 
							
								   * - a webpack configuration object that will be merged into the config
							 | 
						||
| 
								 | 
							
								   * OR
							 | 
						||
| 
								 | 
							
								   * - a function that will receive the raw webpack config.
							 | 
						||
| 
								 | 
							
								   *   the function can either mutate the config directly or return an object
							 | 
						||
| 
								 | 
							
								   *   that will be merged into the config.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param {object | function} fn
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  configureWebpack (fn) {
							 | 
						||
| 
								 | 
							
								    this.service.webpackRawConfigFns.push(fn)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Register a dev serve config function. It will receive the express `app`
							 | 
						||
| 
								 | 
							
								   * instance of the dev server.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param {function} fn
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  configureDevServer (fn) {
							 | 
						||
| 
								 | 
							
								    this.service.devServerConfigFns.push(fn)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Resolve the final raw webpack config, that will be passed to webpack.
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @param {ChainableWebpackConfig} [chainableConfig]
							 | 
						||
| 
								 | 
							
								   * @return {object} Raw webpack config.
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  resolveWebpackConfig (chainableConfig) {
							 | 
						||
| 
								 | 
							
								    return this.service.resolveWebpackConfig(chainableConfig)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Resolve an intermediate chainable webpack config instance, which can be
							 | 
						||
| 
								 | 
							
								   * further tweaked before generating the final raw webpack config.
							 | 
						||
| 
								 | 
							
								   * You can call this multiple times to generate different branches of the
							 | 
						||
| 
								 | 
							
								   * base webpack config.
							 | 
						||
| 
								 | 
							
								   * See https://github.com/mozilla-neutrino/webpack-chain
							 | 
						||
| 
								 | 
							
								   *
							 | 
						||
| 
								 | 
							
								   * @return {ChainableWebpackConfig}
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  resolveChainableWebpackConfig () {
							 | 
						||
| 
								 | 
							
								    return this.service.resolveChainableWebpackConfig()
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * Generate a cache identifier from a number of variables
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  genCacheConfig (id, partialIdentifier, configFiles = []) {
							 | 
						||
| 
								 | 
							
								    const fs = require('fs')
							 | 
						||
| 
								 | 
							
								    const cacheDirectory = this.resolve(`node_modules/.cache/${id}`)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // replace \r\n to \n generate consistent hash
							 | 
						||
| 
								 | 
							
								    const fmtFunc = conf => {
							 | 
						||
| 
								 | 
							
								      if (typeof conf === 'function') {
							 | 
						||
| 
								 | 
							
								        return conf.toString().replace(/\r\n?/g, '\n')
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      return conf
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const variables = {
							 | 
						||
| 
								 | 
							
								      partialIdentifier,
							 | 
						||
| 
								 | 
							
								      'cli-service': require('../package.json').version,
							 | 
						||
| 
								 | 
							
								      'cache-loader': require('cache-loader/package.json').version,
							 | 
						||
| 
								 | 
							
								      env: process.env.NODE_ENV,
							 | 
						||
| 
								 | 
							
								      test: !!process.env.VUE_CLI_TEST,
							 | 
						||
| 
								 | 
							
								      config: [
							 | 
						||
| 
								 | 
							
								        fmtFunc(this.service.projectOptions.chainWebpack),
							 | 
						||
| 
								 | 
							
								        fmtFunc(this.service.projectOptions.configureWebpack)
							 | 
						||
| 
								 | 
							
								      ]
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!Array.isArray(configFiles)) {
							 | 
						||
| 
								 | 
							
								      configFiles = [configFiles]
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    configFiles = configFiles.concat([
							 | 
						||
| 
								 | 
							
								      'package-lock.json',
							 | 
						||
| 
								 | 
							
								      'yarn.lock',
							 | 
						||
| 
								 | 
							
								      'pnpm-lock.yaml'
							 | 
						||
| 
								 | 
							
								    ])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const readConfig = file => {
							 | 
						||
| 
								 | 
							
								      const absolutePath = this.resolve(file)
							 | 
						||
| 
								 | 
							
								      if (!fs.existsSync(absolutePath)) {
							 | 
						||
| 
								 | 
							
								        return
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (absolutePath.endsWith('.js')) {
							 | 
						||
| 
								 | 
							
								        // should evaluate config scripts to reflect environment variable changes
							 | 
						||
| 
								 | 
							
								        try {
							 | 
						||
| 
								 | 
							
								          return JSON.stringify(require(absolutePath))
							 | 
						||
| 
								 | 
							
								        } catch (e) {
							 | 
						||
| 
								 | 
							
								          return fs.readFileSync(absolutePath, 'utf-8')
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        return fs.readFileSync(absolutePath, 'utf-8')
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    variables.configFiles = configFiles.map(file => {
							 | 
						||
| 
								 | 
							
								      const content = readConfig(file)
							 | 
						||
| 
								 | 
							
								      return content && content.replace(/\r\n?/g, '\n')
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const cacheIdentifier = hash(variables)
							 | 
						||
| 
								 | 
							
								    return { cacheDirectory, cacheIdentifier }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = PluginAPI
							 |