@dnlup/vue-cli-plugin-unit-ava
Version:
@vue/cli plugin to run unit tests with ava
248 lines (234 loc) • 6.24 kB
JavaScript
/**
* @module vue-cli-plugin-unit-ava/generator
* @see {@link https://cli.vuejs.org/dev-guide/plugin-dev.html#generator}
*/
const { join } = require('path')
const { stringify } = require('javascript-stringify')
const { devDependencies: injectedPackageDevDeps } = require('../package.json')
/**
* @external GeneratorApi
* @see {@link https://cli.vuejs.org/dev-guide/generator-api.html}
*/
/**
* @param {*} value
* @return {String}
*/
function stringifyJS (value) {
return stringify(value, (val, indent, stringify) => {
if (val && val.__expression) {
return val.__expression
}
return stringify(val)
}, 2)
}
/**
* Add a babel list entry avoiding duplicates
* @param {Array} list - the option list
* @param {Array} entry - the entry
* @retun {Array[]}
*/
function addBabeListEntry (list, entry) {
const index = list.findIndex(i => {
return Array.isArray(i) ? i[0] === entry[0] : i === entry[0]
})
if (index === -1) {
list.push(entry)
}
return list
}
/**
* @param {*} entity
* @return {Array}
*/
function toArray (entity) {
let array = entity || []
if (!Array.isArray(array)) {
array = [array]
}
return array
}
/**
* Merge ava config objects, modifying the first (left) one.
* @param {Object} [left={}]
* @param {Object} [right={}]
* @return {Object} the modified config object
*/
function mergeAvaConfig (left = {}, right = {}) {
for (const key of ['files', 'require', 'extensions']) {
left[key] = toArray(left[key])
right[key] = toArray(right[key])
left[key] = [
...new Set([
...left[key],
...right[key]
])
]
}
left.babel = {
...left.babel,
...right.babel
}
return left
}
/**
* Generator function
* @param {GeneratorApi} api
* @param {Object} options - user options from config prompts
*/
module.exports = (api, options) => {
const root = api.resolve('')
const {
avaConfigLocation,
uiFramework,
styles
} = options
const hasTS = api.hasPlugin('typescript')
const hasBabel = api.hasPlugin('babel')
const isBabelProject = !hasTS && hasBabel
const babelPluginModuleResolver = [
'module-resolver',
{
root: './',
alias: {
'@': './src'
}
}
]
const injectedAvaConfig = {
require: [
'./tests/helpers/setup.js'
]
}
// Configure ava
if (hasTS) {
// Add Typescript configuration to ava
injectedAvaConfig.compileEnhancements = false
injectedAvaConfig.files = [
'tests/unit/**/*.spec.ts'
]
injectedAvaConfig.extensions = [
'ts'
]
// Inject Typescript dependencies
api.extendPackage({
devDependencies: {
'ts-node': injectedPackageDevDeps['ts-node'],
'tsconfig-paths': injectedPackageDevDeps['tsconfig-paths']
}
})
} else if (hasBabel) {
injectedAvaConfig.files = [
'tests/unit/**/*.spec.js'
]
} else if (!hasBabel) {
injectedAvaConfig.babel = false
injectedAvaConfig.compileEnhancements = false
injectedAvaConfig.files = [
'tests/unit/**/*.spec.js'
]
}
api.render(files => {
if (avaConfigLocation === 'ava.config.js') {
const pack = JSON.parse(files['package.json'] || '{}')
if (pack.ava) {
api.exitLog('AVA is already configured in package.json', 'error')
return
}
// TODO: find a way to do not override the configuration.
// Now it is a problem because the `ava.config.js` file
// uses ES6 modules syntax.
files['ava.config.js'] = `export default ${stringifyJS(
injectedAvaConfig)}`
} else {
const avaConfigJs = files['ava.config.js']
if (avaConfigJs) {
api.exitLog('AVA is already configured in ava.config.js', 'error')
return
}
const pack = JSON.parse(files['package.json'] || '{}')
const { ava: config } = pack
const generated = mergeAvaConfig(config, injectedAvaConfig)
api.extendPackage({
ava: generated
}, { merge: false })
}
})
// Configure babel.config.js
if (isBabelProject) {
api.render(files => {
let config = {}
try {
config = require(join(root, 'babel.config.js'))
} catch (error) {
api.exitLog('No `babel.config.js` file found, created a new one.',
'info')
}
config.env = config.env || {}
config.env.test = config.env.test || {}
config.env.test.plugins = config.env.test.plugins || []
config.env.test.presets = config.env.test.presets || []
config.env.test.presets = addBabeListEntry(config.env.test.presets, [
'@vue/app',
{
targets: {
node: 'current'
}
}
])
config.env.test.plugins = addBabeListEntry(
config.env.test.plugins,
babelPluginModuleResolver
)
files['babel.config.js'] = api.genJSConfig(config)
})
api.extendPackage({
devDependencies: {
'require-extension-hooks-babel':
injectedPackageDevDeps['require-extension-hooks-babel'],
'babel-plugin-module-resolver':
injectedPackageDevDeps['babel-plugin-module-resolver']
}
})
}
// Add style loaders
if (styles && styles.length) {
api.extendPackage({
devDependencies: {
'css-modules-require-hook':
injectedPackageDevDeps['css-modules-require-hook']
}
})
if (styles.includes('stylus')) {
api.extendPackage({
devDependencies: {
stylus: injectedPackageDevDeps.stylus
}
})
}
}
// generate assets
api.render('./template', {
hasTS,
hasBabel,
uiFramework,
styles
})
// add common dependencies and scripts
api.extendPackage({
devDependencies: {
'@vue/test-utils':
injectedPackageDevDeps['@vue/test-utils'],
ava:
injectedPackageDevDeps.ava,
'browser-env':
injectedPackageDevDeps['browser-env'],
'require-extension-hooks':
injectedPackageDevDeps['require-extension-hooks'],
'require-extension-hooks-vue':
injectedPackageDevDeps['require-extension-hooks-vue']
},
scripts: {
'test:unit': 'vue-cli-service test:unit'
}
})
}