| 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 |