keep keep keep replace replace keep keep keep keep

this.mainFramebuffer = renderer.createFramebuffer(width, height, this.mainTexture, true);

this.maskFramebuffer = renderer.createFramebuffer(width, height, this.maskTexture, true);

/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2019 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var Class = require('../../utils/Class');

/**
 * @classdesc
 * A Bitmap Mask combines the alpha (opacity) of a masked pixel with the alpha of another pixel.
 * Unlike the Geometry Mask, which is a clipping path, a Bitmap Mask behaves like an alpha mask,
 * not a clipping path. It is only available when using the WebGL Renderer.
 *
 * A Bitmap Mask can use any Game Object to determine the alpha of each pixel of the masked Game Object(s).
 * For any given point of a masked Game Object's texture, the pixel's alpha will be multiplied by the alpha
 * of the pixel at the same position in the Bitmap Mask's Game Object. The color of the pixel from the
 * Bitmap Mask doesn't matter.
 *
 * For example, if a pure blue pixel with an alpha of 0.95 is masked with a pure red pixel with an
 * alpha of 0.5, the resulting pixel will be pure blue with an alpha of 0.475. Naturally, this means
 * that a pixel in the mask with an alpha of 0 will hide the corresponding pixel in all masked Game Objects
 *  A pixel with an alpha of 1 in the masked Game Object will receive the same alpha as the
 * corresponding pixel in the mask.
 *
 * The Bitmap Mask's location matches the location of its Game Object, not the location of the
 * masked objects. Moving or transforming the underlying Game Object will change the mask
 * (and affect the visibility of any masked objects), whereas moving or transforming a masked object
 * will not affect the mask.
 *
 * The Bitmap Mask will not render its Game Object by itself. If the Game Object is not in a
 * Scene's display list, it will only be used for the mask and its full texture will not be directly
 * visible. Adding the underlying Game Object to a Scene will not cause any problems - it will
 * render as a normal Game Object and will also serve as a mask.
 *
 * @class BitmapMask
 * @memberof Phaser.Display.Masks
 * @constructor
 * @since 3.0.0
 *
 * @param {Phaser.Scene} scene - The Scene which this Bitmap Mask will be used in.
 * @param {Phaser.GameObjects.GameObject} renderable - A renderable Game Object that uses a texture, such as a Sprite.
 */
var BitmapMask = new Class({

    initialize:

    function BitmapMask (scene, renderable)
    {
        var renderer = scene.sys.game.renderer;

        /**
         * A reference to either the Canvas or WebGL Renderer that this Mask is using.
         *
         * @name Phaser.Display.Masks.BitmapMask#renderer
         * @type {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)}
         * @since 3.11.0
         */
        this.renderer = renderer;

        /**
         * A renderable Game Object that uses a texture, such as a Sprite.
         *
         * @name Phaser.Display.Masks.BitmapMask#bitmapMask
         * @type {Phaser.GameObjects.GameObject}
         * @since 3.0.0
         */
        this.bitmapMask = renderable;

        /**
         * The texture used for the mask's framebuffer.
         *
         * @name Phaser.Display.Masks.BitmapMask#maskTexture
         * @type {WebGLTexture}
         * @default null
         * @since 3.0.0
         */
        this.maskTexture = null;

        /**
         * The texture used for the main framebuffer.
         *
         * @name Phaser.Display.Masks.BitmapMask#mainTexture
         * @type {WebGLTexture}
         * @default null
         * @since 3.0.0
         */
        this.mainTexture = null;

        /**
         * Whether the Bitmap Mask is dirty and needs to be updated.
         *
         * @name Phaser.Display.Masks.BitmapMask#dirty
         * @type {boolean}
         * @default true
         * @since 3.0.0
         */
        this.dirty = true;

        /**
         * The framebuffer to which a masked Game Object is rendered.
         *
         * @name Phaser.Display.Masks.BitmapMask#mainFramebuffer
         * @type {WebGLFramebuffer}
         * @since 3.0.0
         */
        this.mainFramebuffer = null;

        /**
         * The framebuffer to which the Bitmap Mask's masking Game Object is rendered.
         *
         * @name Phaser.Display.Masks.BitmapMask#maskFramebuffer
         * @type {WebGLFramebuffer}
         * @since 3.0.0
         */
        this.maskFramebuffer = null;

        /**
         * Whether to invert the mask's alpha.
         *
         * If `true`, the alpha of the masking pixel will be inverted before it's multiplied with the masked pixel. Essentially, this means that a masked area will be visible only if the corresponding area in the mask is invisible.
         *
         * @name Phaser.Display.Masks.BitmapMask#invertAlpha
         * @type {boolean}
         * @since 3.1.2
         */
        this.invertAlpha = false;

        if (renderer && renderer.gl)
        {
            var width = renderer.width;
            var height = renderer.height;
            var pot = ((width & (width - 1)) === 0 && (height & (height - 1)) === 0);
            var gl = renderer.gl;
            var wrap = pot ? gl.REPEAT : gl.CLAMP_TO_EDGE;
            var filter = gl.LINEAR;

            this.mainTexture = renderer.createTexture2D(0, filter, filter, wrap, wrap, gl.RGBA, null, width, height);
            this.maskTexture = renderer.createTexture2D(0, filter, filter, wrap, wrap, gl.RGBA, null, width, height);
            this.mainFramebuffer = renderer.createFramebuffer(width, height, this.mainTexture, false);
            this.maskFramebuffer = renderer.createFramebuffer(width, height, this.maskTexture, false);

            renderer.onContextRestored(function (renderer)
            {
                var width = renderer.width;
                var height = renderer.height;
                var pot = ((width & (width - 1)) === 0 && (height & (height - 1)) === 0);
                var gl = renderer.gl;
                var wrap = pot ? gl.REPEAT : gl.CLAMP_TO_EDGE;
                var filter = gl.LINEAR;

                this.mainTexture = renderer.createTexture2D(0, filter, filter, wrap, wrap, gl.RGBA, null, width, height);
                this.maskTexture = renderer.createTexture2D(0, filter, filter, wrap, wrap, gl.RGBA, null, width, height);
                this.mainFramebuffer = renderer.createFramebuffer(width, height, this.mainTexture, false);
                this.maskFramebuffer = renderer.createFramebuffer(width, height, this.maskTexture, false);

            }, this);
        }
    },

    /**
     * Sets a new masking Game Object for the Bitmap Mask.
     *
     * @method Phaser.Display.Masks.BitmapMask#setBitmap
     * @since 3.0.0
     *
     * @param {Phaser.GameObjects.GameObject} renderable - A renderable Game Object that uses a texture, such as a Sprite.
     */
    setBitmap: function (renderable)
    {
        this.bitmapMask = renderable;
    },

    /**
     * Prepares the WebGL Renderer to render a Game Object with this mask applied.
     *
     * This renders the masking Game Object to the mask framebuffer and switches to the main framebuffer so that the masked Game Object will be rendered to it instead of being rendered directly to the frame.
     *
     * @method Phaser.Display.Masks.BitmapMask#preRenderWebGL
     * @since 3.0.0
     *
     * @param {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)} renderer - The WebGL Renderer to prepare.
     * @param {Phaser.GameObjects.GameObject} maskedObject - The masked Game Object which will be drawn.
     * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera to render to.
     */
    preRenderWebGL: function (renderer, maskedObject, camera)
    {
        renderer.pipelines.BitmapMaskPipeline.beginMask(this, maskedObject, camera);
    },

    /**
     * Finalizes rendering of a masked Game Object.
     *
     * This resets the previously bound framebuffer and switches the WebGL Renderer to the Bitmap Mask Pipeline, which uses a special fragment shader to apply the masking effect.
     *
     * @method Phaser.Display.Masks.BitmapMask#postRenderWebGL
     * @since 3.0.0
     *
     * @param {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)} renderer - The WebGL Renderer to clean up.
     */
    postRenderWebGL: function (renderer)
    {
        renderer.pipelines.BitmapMaskPipeline.endMask(this);
    },

    /**
     * This is a NOOP method. Bitmap Masks are not supported by the Canvas Renderer.
     *
     * @method Phaser.Display.Masks.BitmapMask#preRenderCanvas
     * @since 3.0.0
     *
     * @param {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)} renderer - The Canvas Renderer which would be rendered to.
     * @param {Phaser.GameObjects.GameObject} mask - The masked Game Object which would be rendered.
     * @param {Phaser.Cameras.Scene2D.Camera} camera - The Camera to render to.
     */
    preRenderCanvas: function ()
    {
        // NOOP
    },

    /**
     * This is a NOOP method. Bitmap Masks are not supported by the Canvas Renderer.
     *
     * @method Phaser.Display.Masks.BitmapMask#postRenderCanvas
     * @since 3.0.0
     *
     * @param {(Phaser.Renderer.Canvas.CanvasRenderer|Phaser.Renderer.WebGL.WebGLRenderer)} renderer - The Canvas Renderer which would be rendered to.
     */
    postRenderCanvas: function ()
    {
        // NOOP
    },

    /**
     * Destroys this BitmapMask and nulls any references it holds.
     *
     * Note that if a Game Object is currently using this mask it will _not_ automatically detect you have destroyed it,
     * so be sure to call `clearMask` on any Game Object using it, before destroying it.
     *
     * @method Phaser.Display.Masks.BitmapMask#destroy
     * @since 3.7.0
     */
    destroy: function ()
    {
        this.bitmapMask = null;

        var renderer = this.renderer;

        if (renderer && renderer.gl)
        {
            renderer.deleteTexture(this.mainTexture);
            renderer.deleteTexture(this.maskTexture);
            renderer.deleteFramebuffer(this.mainFramebuffer);
            renderer.deleteFramebuffer(this.maskFramebuffer);
        }

        this.mainTexture = null;
        this.maskTexture = null;
        this.mainFramebuffer = null;
        this.maskFramebuffer = null;
        this.renderer = null;
    }

});

module.exports = BitmapMask;


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2019 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

/**
 * The Loader Plugin Progress Event.
 *
 * This event is dispatched when the Loader updates its load progress, typically as a result of a file having completed loading.
 *
 * Listen to it from a Scene using: `this.load.on('progress', listener)`.
 *
 * @event Phaser.Loader.Events#PROGRESS
 * @since 3.0.0
 *
 * @param {number} progress - The current progress of the load. A value between 0 and 1.
 */
module.exports = 'progress';


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2019 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

/**
 * Elastic ease-in.
 *
 * @function Phaser.Math.Easing.Elastic.In
 * @since 3.0.0
 *
 * @param {number} v - The value to be tweened.
 * @param {number} [amplitude=0.1] - The amplitude of the elastic ease.
 * @param {number} [period=0.1] - Sets how tight the sine-wave is, where smaller values are tighter waves, which result in more cycles.
 *
 * @return {number} The tweened value.
 */
var In = function (v, amplitude, period)
{
    if (amplitude === undefined) { amplitude = 0.1; }
    if (period === undefined) { period = 0.1; }

    if (v === 0)
    {
        return 0;
    }
    else if (v === 1)
    {
        return 1;
    }
    else
    {
        var s = period / 4;

        if (amplitude < 1)
        {
            amplitude = 1;
        }
        else
        {
            s = period * Math.asin(1 / amplitude) / (2 * Math.PI);
        }

        return -(amplitude * Math.pow(2, 10 * (v -= 1)) * Math.sin((v - s) * (2 * Math.PI) / period));
    }
};

module.exports = In;


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2019 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var CollisionInfo = require('./CollisionInfo');
var CONST = require('./const');

/**
 * Takes a CollisionInfo object and tests to see if the two bodies are still intersecting / touching.
 *
 * @function Phaser.Physics.Arcade.CheckOverlap
 * @since 3.17.0
 *
 * @param {Phaser.Physics.Arcade.Body} body1 - The first Body to separate.
 *
 * @return {boolean}
 */
var CheckOverlap = function (collisionInfo)
{
    collisionInfo = CollisionInfo.update(collisionInfo);

    var face = collisionInfo.face;
    var body1 = collisionInfo.body1;
    var body2 = collisionInfo.body2;

    // console.log('');
    // console.log('%c CheckOverlap ' + body1.world._frame + '                                                                                ', 'background-color: pink');
    // collisionInfo.dump();

    switch (face)
    {
        case CONST.FACING_LEFT:

            body1.setTouchingLeft();
            body2.setTouchingRight();

            body1.setBlockedLeft();
            body2.setBlockedRight();

            if (body1.isWorldBlockedRight())
            {
                body2.setHardBlockedRight();
            }

            if (body2.isWorldBlockedLeft())
            {
                body1.setHardBlockedLeft();
            }

            break;

        case CONST.FACING_RIGHT:

            body1.setTouchingRight();
            body2.setTouchingLeft();

            body1.setBlockedRight();
            body2.setBlockedLeft();

            if (body1.isWorldBlockedLeft())
            {
                body2.setHardBlockedLeft();
            }

            if (body2.isWorldBlockedRight())
            {
                body1.setHardBlockedRight();
            }

            break;

        case CONST.FACING_UP:

            body1.setTouchingUp();
            body2.setTouchingDown();

            body1.setBlockedUp();
            body2.setBlockedDown();

            if (body1.isWorldBlockedDown())
            {
                body2.setHardBlockedDown();
            }

            if (body2.isWorldBlockedUp())
            {
                body1.setHardBlockedUp();
            }

            break;

        case CONST.FACING_DOWN:

            body1.setTouchingDown();
            body2.setTouchingUp();

            body1.setBlockedDown();
            body2.setBlockedUp();

            if (body1.isWorldBlockedUp())
            {
                body2.setHardBlockedUp();
            }

            if (body2.isWorldBlockedDown())
            {
                body1.setHardBlockedDown();
            }

            break;
    }

    return collisionInfo.touching;
};

module.exports = CheckOverlap;


keep keep keep replace replace keep keep keep


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2019 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

/**
 * Adds the given item, or array of items, to the array starting at the index specified.
 *
 * Each item must be unique within the array.
 *
 * Existing elements in the array are shifted up.
 *
 * The array is modified in-place and returned.
 *
 * You can optionally specify a limit to the maximum size of the array. If the quantity of items being
 * added will take the array length over this limit, it will stop adding once the limit is reached.
 *
 * You can optionally specify a callback to be invoked for each item successfully added to the array.
 *
 * @function Phaser.Utils.Array.AddAt
 * @since 3.4.0
 *
 * @param {array} array - The array to be added to.
 * @param {any|any[]} item - The item, or array of items, to add to the array.
 * @param {integer} [index=0] - The index in the array where the item will be inserted.
 * @param {integer} [limit] - Optional limit which caps the size of the array.
 * @param {function} [callback] - A callback to be invoked for each item successfully added to the array.
 * @param {object} [context] - The context in which the callback is invoked.
 *
 * @return {array} The input array.
 */
var AddAt = function (array, item, index, limit, callback, context)
{
    if (index === undefined) { index = 0; }
    if (context === undefined) { context = array; }

    if (limit > 0)
    {
        var remaining = limit - array.length;

        //  There's nothing more we can do here, the array is full
        if (remaining <= 0)
        {
            return null;
        }
    }

    //  Fast path to avoid array mutation and iteration
    if (!Array.isArray(item))
    {
        if (array.indexOf(item) === -1)
        {
            array.splice(index, 0, item);

            if (callback)
            {
                callback.call(context, item);
            }

            return item;
        }
        else
        {
            return null;
        }
    }

    //  If we got this far, we have an array of items to insert

    //  Ensure all the items are unique
    var itemLength = item.length - 1;

    while (itemLength >= 0)
    {
        if (array.indexOf(item[itemLength]) !== -1)
        {
            //  Already exists in array, so remove it
            item.pop();
        }

        itemLength--;
    }

    //  Anything left?
    itemLength = item.length;

    if (itemLength === 0)
    {
        return null;
    }

    //  Truncate to the limit
    if (limit > 0 && itemLength > remaining)
    {
        item.splice(remaining);

        itemLength = remaining;
    }

    for (var i = itemLength - 1; i >= 0; i--)
    {
        var entry = item[i];

        array.splice(index, 0, entry);

        if (callback)
        {
            callback.call(context, entry);
        }
    }

    return item;
};

module.exports = AddAt;


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2019 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

/**
 * Compares the `x`, `y`, `width` and `height` properties of the two given Ellipses.
 * Returns `true` if they all match, otherwise returns `false`.
 *
 * @function Phaser.Geom.Ellipse.Equals
 * @since 3.0.0
 *
 * @param {Phaser.Geom.Ellipse} ellipse - The first Ellipse to compare.
 * @param {Phaser.Geom.Ellipse} toCompare - The second Ellipse to compare.
 *
 * @return {boolean} `true` if the two Ellipse equal each other, otherwise `false`.
 */
var Equals = function (ellipse, toCompare)
{
    return (
        ellipse.x === toCompare.x &&
        ellipse.y === toCompare.y &&
        ellipse.width === toCompare.width &&
        ellipse.height === toCompare.height
    );
};

module.exports = Equals;


/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2019 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var GetValue = require('../object/GetValue');
var Shuffle = require('./Shuffle');

var BuildChunk = function (a, b, qty)
{
    var out = [];

    for (var aIndex = 0; aIndex < a.length; aIndex++)
    {
        for (var bIndex = 0; bIndex < b.length; bIndex++)
        {
            for (var i = 0; i < qty; i++)
            {
                out.push({ a: a[aIndex], b: b[bIndex] });
            }
        }
    }

    return out;
};

/**
 * Creates an array populated with a range of values, based on the given arguments and configuration object.
 *
 * Range ([a,b,c], [1,2,3]) =
 * a1, a2, a3, b1, b2, b3, c1, c2, c3
 *
 * Range ([a,b], [1,2,3], qty = 3) =
 * a1, a1, a1, a2, a2, a2, a3, a3, a3, b1, b1, b1, b2, b2, b2, b3, b3, b3
 *
 * Range ([a,b,c], [1,2,3], repeat x1) =
 * a1, a2, a3, b1, b2, b3, c1, c2, c3, a1, a2, a3, b1, b2, b3, c1, c2, c3
 *
 * Range ([a,b], [1,2], repeat -1 = endless, max = 14) =
 * Maybe if max is set then repeat goes to -1 automatically?
 * a1, a2, b1, b2, a1, a2, b1, b2, a1, a2, b1, b2, a1, a2 (capped at 14 elements)
 *
 * Range ([a], [1,2,3,4,5], random = true) =
 * a4, a1, a5, a2, a3
 *
 * Range ([a, b], [1,2,3], random = true) =
 * b3, a2, a1, b1, a3, b2
 *
 * Range ([a, b, c], [1,2,3], randomB = true) =
 * a3, a1, a2, b2, b3, b1, c1, c3, c2
 *
 * Range ([a], [1,2,3,4,5], yoyo = true) =
 * a1, a2, a3, a4, a5, a5, a4, a3, a2, a1
 *
 * Range ([a, b], [1,2,3], yoyo = true) =
 * a1, a2, a3, b1, b2, b3, b3, b2, b1, a3, a2, a1
 *
 * @function Phaser.Utils.Array.Range
 * @since 3.0.0
 *
 * @param {array} a - The first array of range elements.
 * @param {array} b - The second array of range elements.
 * @param {object} [options] - A range configuration object. Can contain: repeat, random, randomB, yoyo, max, qty.
 *
 * @return {array} An array of arranged elements.
 */
var Range = function (a, b, options)
{
    var max = GetValue(options, 'max', 0);
    var qty = GetValue(options, 'qty', 1);
    var random = GetValue(options, 'random', false);
    var randomB = GetValue(options, 'randomB', false);
    var repeat = GetValue(options, 'repeat', 0);
    var yoyo = GetValue(options, 'yoyo', false);

    var out = [];

    if (randomB)
    {
        Shuffle(b);
    }

    //  Endless repeat, so limit by max
    if (repeat === -1)
    {
        if (max === 0)
        {
            repeat = 0;
        }
        else
        {
            //  Work out how many repeats we need
            var total = (a.length * b.length) * qty;

            if (yoyo)
            {
                total *= 2;
            }

            repeat = Math.ceil(max / total);
        }
    }

    for (var i = 0; i <= repeat; i++)
    {
        var chunk = BuildChunk(a, b, qty);

        if (random)
        {
            Shuffle(chunk);
        }

        out = out.concat(chunk);

        if (yoyo)
        {
            chunk.reverse();

            out = out.concat(chunk);
        }
    }

    if (max)
    {
        out.splice(max);
    }

    return out;
};

module.exports = Range;


keep keep replace replace replace keep keep keep keep

module.exports = api => {
  api.extendPackage({
    devDependencies: {
      '@vue/babel-preset-app': '^3.0.0-alpha.3'
    },
    babel: {
      presets: ['@vue/app']
    }
  })
}


module.exports = api => {
  api.chainWebpack(webpackConfig => {
    if (process.env.NODE_ENV === 'development') {
      webpackConfig
        .devtool('cheap-module-eval-source-map')
        .output
          .publicPath('/')

      webpackConfig
        .plugin('hmr')
          .use(require('webpack/lib/HotModuleReplacementPlugin'))

      webpackConfig
        .plugin('named-modules')
          .use(require('webpack/lib/NamedModulesPlugin'))

      webpackConfig
        .plugin('no-emit-on-errors')
          .use(require('webpack/lib/NoEmitOnErrorsPlugin'))

      // friendly error plugin displays very confusing errors when webpack
      // fails to resolve a loader, so we provide custom handlers to improve it
      const { transformer, formatter } = require('../webpack/resolveLoaderError')
      webpackConfig
        .plugin('firendly-errors')
          .use(require('friendly-errors-webpack-plugin'), [{
            additionalTransformers: [transformer],
            additionalFormatters: [formatter]
          }])

      webpackConfig
        .plugin('watch-missing')
          .use(
            require('../webpack/WatchMissingNodeModulesPlugin'),
            [api.resolve('node_modules')]
          )
    }
  })
}


keep keep keep keep keep replace replace keep keep keep keep keep

require('../lib/invoke') // so that jest registers the file for this test
const create = require('@vue/cli-test-utils/createTestProject')

test('invoke single generator', async () => {
  const project = await create('invoke', {
    plugins: {
      '@vue/cli-plugin-babel': {}
    }
  })
  // mock install
  const pkg = JSON.parse(await project.read('package.json'))
  pkg.devDependencies['@vue/cli-plugin-eslint'] = '*'
  await project.write('package.json', JSON.stringify(pkg, null, 2))

  const cliBinPath = require.resolve('../bin/vue')
  await project.run(`${cliBinPath} invoke eslint --config airbnb --lintOn save,commit`)

  const updatedPkg = JSON.parse(await project.read('package.json'))
  expect(updatedPkg.scripts.lint).toBe('vue-cli-service lint')
  expect(updatedPkg.devDependencies).toHaveProperty('eslint-plugin-vue')
  expect(updatedPkg.devDependencies).toHaveProperty('lint-staged')
  expect(updatedPkg.eslintConfig).toEqual({
    extends: ['plugin:vue/essential', '@vue/airbnb']
  })
  expect(updatedPkg.gitHooks).toEqual({
    'pre-commit': 'lint-staged'
  })

  const lintedMain = await project.read('src/main.js')
  expect(lintedMain).toMatch(';') // should've been linted in post-generate hook
})


const path = require('path')
const resolve = require('resolve')
const { findExisting } = require('./util')

module.exports = function createConfigPlugin (context, entry) {
  return {
    id: '@vue/cli-service-global-config',
    apply: (api, options) => {
      api.chainWebpack(config => {
        // entry is *.vue file, create alias for built-in js entry
        if (/\.vue$/.test(entry)) {
          config.resolve
            .alias
              .set('~entry', path.resolve(context, entry))
          entry = require.resolve('../template/main.js')
        } else {
          // make sure entry is relative
          if (!/^\.\//.test(entry)) {
            entry = `./${entry}`
          }
        }

        // ensure loaders can be resolved properly
        // this is done by locating vue's install location (which is a
        // dependency of the global service)
        const modulePath = path.resolve(require.resolve('vue'), '../../../')
        config.resolveLoader
          .modules
            .add(modulePath)

        // add resolve alias for vue and vue-hot-reload-api
        // but prioritize versions installed locally.
        try {
          resolve.sync('vue', { basedir: context })
        } catch (e) {
          const vuePath = path.dirname(require.resolve('vue'))
          config.resolve.alias
            .set('vue$', `${vuePath}/${options.compiler ? `vue.esm.js` : `vue.runtime.esm.js`}`)
        }

        try {
          resolve.sync('vue-hot-reload-api', { basedir: context })
        } catch (e) {
          config.resolve.alias
            .set('vue-hot-reload-api', require.resolve('vue-hot-reload-api'))
        }

        // set entry
        config
          .entry('app')
            .clear()
            .add(entry)

        const babelOptions = {
          presets: [require.resolve('@vue/babel-preset-app')]
        }

        // set inline vue-loader options
        config.module
          .rule('vue')
            .use('vue-loader')
              .tap(options => {
                options.loaders.js[1].options = babelOptions
                return options
              })

        // set inline babel options
        config.module
          .rule('js')
            .include
              .clear()
              .end()
            .exclude
              .add(/node_modules/)
              .end()
            .use('babel-loader')
              .tap(() => babelOptions)

        // check eslint config presence
        // otherwise eslint-loader goes all the way up to look for eslintrc, can be
        // messed up when the project is inside another project.
        const ESLintConfigFile = findExisting(context, [
          '.eslintrc.js',
          '.eslintrc.yaml',
          '.eslintrc.yml',
          '.eslintrc.json',
          '.eslintrc',
          'package.json'
        ])
        const hasESLintConfig = ESLintConfigFile === 'package.json'
          ? !!(require(path.join(context, 'package.json')).eslintConfig)
          : !!ESLintConfigFile

        // set inline eslint options
        config.module
          .rule('eslint')
            .include
              .clear()
              .end()
            .exclude
              .add(/node_modules/)
              .end()
            .use('eslint-loader')
              .tap(options => Object.assign({}, options, {
                useEslintrc: hasESLintConfig,
                baseConfig: {
                  extends: [
                    'plugin:vue/essential',
                    'eslint:recommended'
                  ]
                }
              }))

        // set html plugin template
        const indexFile = findExisting(context, [
          'index.html',
          'public/index.html'
        ]) || path.resolve(__dirname, '../template/index.html')
        config
          .plugin('html')
            .tap(() => [{ template: indexFile }])

        // disable copy plugin if no public dir
        if (!findExisting(context, ['public'])) {
          config.plugins.delete('copy')
        }
      })
    }
  }
}


const chalk = require('chalk')
const TYPE = 'cant-resolve-loader'
const errorRE = /Can't resolve '(.*loader)'/

exports.transformer = error => {
  if (error.webpackError) {
    const match = error.webpackError.message.match(errorRE)
    if (match) {
      return Object.assign({}, error, {
        type: TYPE,
        loader: match[1]
      })
    }
  }
  return error
}

exports.formatter = errors => {
  errors = errors.filter(e => e.type === TYPE)
  if (errors.length) {
    return errors.map(e => {
      return `Failed to resolve loader: ${chalk.yellow(e.loader)}`
    }).concat(`\nYou may need to install the missing loader.`)
  }
}


module.exports = api => {
  api.registerCommand('test', {
    description: 'run unit tests with jest',
    usage: 'vue-cli-service test [options] <regexForTestFiles>',
    options: {
      '--watch': 'run tests in watch mode'
    },
    details:
      `All jest command line options are supported.\n` +
      `See https://facebook.github.io/jest/docs/en/cli.html for more details.`
  }, (args, rawArgv) => {
    api.setMode('test')
    // for @vue/babel-preset-app
    process.env.VUE_CLI_BABEL_TARGET_NODE = true
    process.env.VUE_CLI_BABEL_TRANSPILE_MODULES = true

    const execa = require('execa')
    const jestBinPath = require.resolve('jest/bin/jest')

    let testMatch = []
    if (!args._.length && api.hasPlugin('typescript')) {
      testMatch = [`--testMatch`, `<rootDir>/**/*.spec.(ts|tsx|js)`]
    }

    const argv = [
      ...rawArgv,
      ...testMatch
    ]

    return new Promise((resolve, reject) => {
      const child = execa(jestBinPath, argv, {
        cwd: api.resolve('.'),
        stdio: 'inherit'
      })
      child.on('error', reject)
      child.on('exit', code => {
        if (code !== 0) {
          reject(`jest exited with code ${code}.`)
        } else {
          resolve()
        }
      })
    })
  })
}


keep keep keep keep replace keep keep keep

const generateWithPlugin = require('@vue/cli-test-utils/generateWithPlugin')

test('base', async () => {
  const { pkg } = await generateWithPlugin({
    id: 'eslint',
    apply: require('../generator'),
    options: {}
  })

  expect(pkg.scripts.lint).toBeTruthy()
  expect(pkg.eslintConfig).toEqual({
    extends: ['plugin:vue/essential', 'eslint:recommended']
  })
  expect(pkg.devDependencies).toHaveProperty('eslint-plugin-vue')
})

test('airbnb', async () => {
  const { pkg } = await generateWithPlugin({
    id: 'eslint',
    apply: require('../generator'),
    options: {
      config: 'airbnb'
    }
  })

  expect(pkg.scripts.lint).toBeTruthy()
  expect(pkg.eslintConfig).toEqual({
    extends: ['plugin:vue/essential', '@vue/airbnb']
  })
  expect(pkg.devDependencies).toHaveProperty('eslint-plugin-vue')
  expect(pkg.devDependencies).toHaveProperty('@vue/eslint-config-airbnb')
})

test('standard', async () => {
  const { pkg } = await generateWithPlugin({
    id: 'eslint',
    apply: require('../generator'),
    options: {
      config: 'standard'
    }
  })

  expect(pkg.scripts.lint).toBeTruthy()
  expect(pkg.eslintConfig).toEqual({
    extends: ['plugin:vue/essential', '@vue/standard']
  })
  expect(pkg.devDependencies).toHaveProperty('eslint-plugin-vue')
  expect(pkg.devDependencies).toHaveProperty('@vue/eslint-config-standard')
})

test('prettier', async () => {
  const { pkg } = await generateWithPlugin({
    id: 'eslint',
    apply: require('../generator'),
    options: {
      config: 'prettier'
    }
  })

  expect(pkg.scripts.lint).toBeTruthy()
  expect(pkg.eslintConfig).toEqual({
    extends: ['plugin:vue/essential', '@vue/prettier']
  })
  expect(pkg.devDependencies).toHaveProperty('eslint-plugin-vue')
  expect(pkg.devDependencies).toHaveProperty('@vue/eslint-config-prettier')
})

test('lint on save', async () => {
  const { pkg } = await generateWithPlugin({
    id: 'eslint',
    apply: require('../generator'),
    options: {
      lintOn: 'save'
    }
  })

  expect(pkg.vue).toEqual({
    lintOnSave: true
  })
})

test('lint on commit', async () => {
  const { pkg } = await generateWithPlugin({
    id: 'eslint',
    apply: require('../generator'),
    options: {
      lintOn: 'commit'
    }
  })
  expect(pkg.gitHooks['pre-commit']).toBe('lint-staged')
  expect(pkg.devDependencies).toHaveProperty('lint-staged')
  expect(pkg['lint-staged']).toEqual({
    '*.js': ['vue-cli-service lint', 'git add'],
    '*.vue': ['vue-cli-service lint', 'git add']
  })
})


jest.setTimeout(40000)

const create = require('@vue/cli-test-utils/createTestProject')

test('should work', async () => {
  const project = await create('e2e-cypress', {
    plugins: {
      '@vue/cli-plugin-babel': {},
      '@vue/cli-plugin-e2e-cypress': {}
    }
  })

  const config = JSON.parse(await project.read('cypress.json'))
  config.videoRecording = false
  await project.write('cypress.json', JSON.stringify(config))

  await project.run(`vue-cli-service e2e`)
})


// using this requires mocking fs & inquirer

const Creator = require('@vue/cli/lib/Creator')
const { expectPrompts } = require('inquirer') // from mock

module.exports = async function assertPromptModule (
  module,
  expectedPrompts,
  expectedOptions,
  opts = {}
) {
  // auto fill non-module prompts
  if (opts.plguinsOnly) {
    expectedPrompts.unshift(
      {
        message: 'project creation mode',
        choose: 1
      }
    )
    expectedPrompts.push(
      {
        message: 'package manager',
        choose: 0
      },
      {
        message: 'Save the preferences',
        confirm: false
      }
    )
  }

  expectPrompts(expectedPrompts)
  const creator = new Creator('test', '/', [].concat(module))
  const options = await creator.promptAndResolveOptions()

  if (opts.plguinsOnly) {
    delete options.packageManager
  }
  expect(options).toEqual(expectedOptions)
}


const defaults = {
  name: 'PWA app',
  themeColor: '#4DBA87', // The Vue color
  msTileColor: '#000000'
}

module.exports = class HtmlPwaPlugin {
  constructor (options = {}) {
    this.options = Object.assign({}, defaults, options)
  }

  apply (compiler) {
    compiler.plugin('compilation', compilation => {
      compilation.plugin('html-webpack-plugin-before-html-processing', (data, cb) => {
        // wrap favicon in the base template with IE only comment
        data.html = data.html.replace(/<link rel="shortcut icon"[^>]+>/, '<!--[if IE]>$&<![endif]-->')
        cb(null, data)
      })

      compilation.plugin('html-webpack-plugin-alter-asset-tags', (data, cb) => {
        const { name, themeColor, msTileColor } = this.options
        const { publicPath } = compiler.options.output

        data.head.push(
          // Favicons
          makeTag('link', {
            rel: 'icon',
            type: 'image/png',
            sizes: '32x32',
            href: `${publicPath}img/icons/favicon-32x32.png`
          }),
          makeTag('link', {
            rel: 'icon',
            type: 'image/png',
            sizes: '16x16',
            href: `${publicPath}img/icons/favicon-16x16.png`
          }),

          // Add to home screen for Android and modern mobile browsers
          makeTag('link', {
            rel: 'manifest',
            href: `${publicPath}manifest.json`
          }),
          makeTag('meta', {
            name: 'theme-color',
            content: themeColor
          }),

          // Add to home screen for Safari on iOS
          makeTag('meta', {
            name: 'apple-mobile-web-app-capable',
            content: 'yes'
          }),
          makeTag('meta', {
            name: 'apple-mobile-web-app-status-bar-style',
            content: 'black'
          }),
          makeTag('meta', {
            name: 'apple-mobile-web-app-title',
            content: name
          }),
          makeTag('link', {
            rel: 'apple-touch-icon',
            href: `${publicPath}img/icons/apple-touch-icon-152x152.png`
          }),
          makeTag('link', {
            rel: 'mask-icon',
            href: `${publicPath}img/icons/safari-pinned-tab.svg`,
            color: themeColor
          }),

          // Add to home screen for Windows
          makeTag('meta', {
            name: 'msapplication-TileImage',
            content: `${publicPath}img/icons/msapplication-icon-144x144.png`
          }),
          makeTag('meta', {
            name: 'msapplication-TileColor',
            content: msTileColor
          })
        )

        cb(null, data)
      })
    })
  }
}

function makeTag (tagName, attributes, closeTag = false) {
  return {
    tagName,
    closeTag,
    attributes
  }
}


@vue/cli-plugin-babel

babel plugin for vue-cli


// https://docs.cypress.io/api/introduction/api.html

describe('My First Test', () => {
  it('Visits the Kitchen Sink', () => {
    cy.visit(Cypress.env('VUE_DEV_SERVER_URL'))
    cy.contains('h1', 'Welcome to Your Vue.js <%- hasTS ? '+ TypeScript ' : '' %>App')
  })
})


const chalk = require('chalk')
const version = require('../../package.json').version
const { clearConsole } = require('@vue/cli-shared-utils')

let title = chalk.bold.green(`Vue CLI v${version}`)
if (process.env.VUE_CLI_TEST) {
  title += ' ' + chalk.blue.bold('TEST')
}
if (process.env.VUE_CLI_DEBUG) {
  title += ' ' + chalk.magenta.bold('DEBUG')
}

module.exports = () => clearConsole(title)


const prefixRE = /^VUE_APP_/

module.exports = function resolveClientEnv (publicPath) {
  const env = {}
  Object.keys(process.env).forEach(key => {
    if (prefixRE.test(key) || key === 'NODE_ENV') {
      env[key] = JSON.stringify(process.env[key])
    }
  })
  env.BASE_URL = JSON.stringify(publicPath)
  return {
    'process.env': env
  }
}


const { pkg } = await generateWithPlugin({

keep keep keep keep keep replace keep keep keep keep keep

@vue/cli-plugin-typescript

typescript plugin for vue-cli


// dev only

const fs = require('fs')
const path = require('path')
const { linkBin } = require('@vue/cli-shared-utils')

module.exports = function setupDevProject (targetDir, deps) {
  const pkg = require(path.resolve(targetDir, 'package.json'))
  pkg.devDependencies = {}
  deps.forEach(dep => {
    pkg.devDependencies[dep] = require(path.resolve(
      __dirname,
      '../../../../',
      dep,
      'package.json'
    )).version
  })
  fs.writeFileSync(
    path.resolve(targetDir, 'package.json'),
    JSON.stringify(pkg, null, 2)
  )
  return linkBin(
    require.resolve('@vue/cli-service/bin/vue-cli-service'),
    path.join(targetDir, 'node_modules', '.bin', 'vue-cli-service')
  )
}


module.exports = (api, { lintOnSave }) => {
  if (lintOnSave) {
    const options = require('./eslintOptions')
    api.chainWebpack(webpackConfig => {
      webpackConfig.module
        .rule('eslint')
          .pre()
          .include
            .add(api.resolve('src'))
            .add(api.resolve('test'))
            .end()
          .test(/\.(vue|jsx?)$/)
          .use('eslint-loader')
            .loader('eslint-loader')
            .options(Object.assign(options, {
              formatter: require('eslint/lib/formatters/codeframe')
            }))
    })
  }

  api.registerCommand('lint', {
    descriptions: 'lint source files',
    usage: 'vue-cli-service lint [options] [...files]',
    options: {
      '--format [formatter]': 'specify formatter (default: codeframe)',
      '--no-fix': 'do not fix errors'
    },
    details: 'For more options, see https://eslint.org/docs/user-guide/command-line-interface#options'
  }, args => {
    require('./lint')(api.resolve('.'), args)
  })
}


module.exports = (api, { config, lintOn = [] }) => {
  if (typeof lintOn === 'string') {
    lintOn = lintOn.split(',')
  }

  const pkg = {
    scripts: {
      lint: 'vue-cli-service lint'
    },
    eslintConfig: {
      extends: ['plugin:vue/essential']
    },
    devDependencies: {
      'eslint-plugin-vue': '^4.2.0'
    }
  }

  if (config === 'airbnb') {
    pkg.eslintConfig.extends.push('@vue/airbnb')
    Object.assign(pkg.devDependencies, {
      '@vue/eslint-config-airbnb': '^3.0.0-alpha.3'
    })
  } else if (config === 'standard') {
    pkg.eslintConfig.extends.push('@vue/standard')
    Object.assign(pkg.devDependencies, {
      '@vue/eslint-config-standard': '^3.0.0-alpha.3'
    })
  } else if (config === 'prettier') {
    pkg.eslintConfig.extends.push('@vue/prettier')
    Object.assign(pkg.devDependencies, {
      '@vue/eslint-config-prettier': '^3.0.0-alpha.3'
    })
  } else {
    // default
    pkg.eslintConfig.extends.push('eslint:recommended')
  }

  if (lintOn.includes('save')) {
    pkg.vue = {
      lintOnSave: true // eslint-loader configured in runtime plugin
    }
  }

  if (lintOn.includes('commit')) {
    Object.assign(pkg.devDependencies, {
      'lint-staged': '^6.0.0'
    })
    pkg.gitHooks = {
      'pre-commit': 'lint-staged'
    }
    pkg['lint-staged'] = {
      '*.js': ['vue-cli-service lint', 'git add'],
      '*.vue': ['vue-cli-service lint', 'git add']
    }
  }

  api.extendPackage(pkg)

  if (api.hasPlugin('unit-mocha')) {
    api.render(files => {
      files['test/unit/.eslintrc'] = JSON.stringify({
        env: { mocha: true }
      }, null, 2)
    })
  } else if (api.hasPlugin('unit-jest')) {
    api.render(files => {
      files['test/unit/.eslintrc'] = JSON.stringify({
        env: { jest: true }
      }, null, 2)
    })
  }

  // lint & fix after create to ensure files adhere to chosen config
  if (config && config !== 'base') {
    api.onCreateComplete(() => {
      require('./lint')(api.resolve('.'), { silent: true })
    })
  }
}


module.exports = api => {
  api.chainWebpack(webpackConfig => {
    if (process.env.NODE_ENV === 'test') {
      webpackConfig.merge({
        devtool: 'inline-cheap-module-source-map',
        externals: [require('webpack-node-externals')()]
      })
    }
  })

  api.registerCommand('test', {
    description: 'run unit tests with mocha-webpack',
    usage: 'vue-cli-service test [options] [...files]',
    options: {
      '--watch, -w': 'run in watch mode',
      '--grep, -g': 'only run tests matching <pattern>',
      '--slow, -s': '"slow" test threshold in milliseconds',
      '--timeout, -t': 'timeout threshold in milliseconds',
      '--bail, -b': 'bail after first test failure',
      '--require, -r': 'require the given module before running tests',
      '--include': 'include the given module into test bundle'
    },
    details: (
      `The above list only includes the most commonly used options.\n` +
      `For a full list of available options, see\n` +
      `http://zinserjan.github.io/mocha-webpack/docs/installation/cli-usage.html`
    )
  }, (args, rawArgv) => {
    api.setMode('test')
    // for @vue/babel-preset-app
    process.env.VUE_CLI_BABEL_TARGET_NODE = true
    // start runner
    const execa = require('execa')
    const bin = require.resolve('mocha-webpack/bin/mocha-webpack')
    const argv = rawArgv.concat([
      '--recursive',
      '--require',
      require.resolve('./setup.js'),
      '--webpack-config',
      require.resolve('@vue/cli-service/webpack.config.js')
    ])

    return new Promise((resolve, reject) => {
      const child = execa(bin, argv, { stdio: 'inherit' })
      child.on('error', reject)
      child.on('exit', code => {
        if (code !== 0) {
          reject(`mocha-webpack exited with code ${code}.`)
        } else {
          resolve()
        }
      })
    })
  })
}


module.exports = (api, options) => {
  const cacheDirectory = api.resolve('node_modules/.cache/cache-loader')

  api.chainWebpack(config => {
    config.entry('app')
      .clear()
      .add('./src/main.ts')

    config.resolve
      .extensions
        .merge(['.ts', '.tsx'])

    const tsRule = config.module
      .rule('ts')
        .test(/\.tsx?$/)
        .include
          .add(api.resolve('src'))
          .add(api.resolve('test'))
          .end()

    if (options.experimentalCompileTsWithBabel) {
      // Experimental: compile TS with babel so that it can leverage
      // preset-env for auto-detected polyfills based on browserslists config.
      // this is pending on the readiness of @babel/preset-typescript.
      tsRule
        .use('cache-loader')
          .loader('cache-loader')
          .options({ cacheDirectory })
          .end()
        .use('babel-loader')
          .loader('babel-loader')

      config.module
        .rule('vue')
          .use('vue-loader')
            .tap(options => {
              options.loaders.ts = 'babel-loader'
              return options
            })
    } else {
      if (api.hasPlugin('babel')) {
        tsRule
          .use('cache-loader')
            .loader('cache-loader')
            .options({ cacheDirectory })
            .end()
          .use('babel-loader')
            .loader('babel-loader')
      }
      tsRule
        .use('cache-loader-2')
          .loader('cache-loader')
          .options({ cacheDirectory })
          .end()
        .use('ts-loader')
          .loader('ts-loader')
          .options({
            transpileOnly: true,
            appendTsSuffixTo: [/\.vue$/]
          })
    }

    config
      .plugin('fork-ts-checker')
        .use(require('fork-ts-checker-webpack-plugin'), [{
          vue: true,
          tslint: options.lintOnSave,
          formatter: 'codeframe'
        }])
  })

  api.registerCommand('lint', {
    descriptions: 'lint source files with TSLint',
    usage: 'vue-cli-service lint [options] [...files]',
    options: {
      '--format [formatter]': 'specify formatter (default: codeFrame)',
      '--no-fix': 'do not fix errors',
      '--formatters-dir [dir]': 'formatter directory',
      '--rules-dir [dir]': 'rules directory'
    }
  }, args => {
    return require('./lib/tslint')(args, api)
  })
}


jest.setTimeout(30000)

const path = require('path')
const { linkBin } = require('@vue/cli-shared-utils')
const create = require('@vue/cli-test-utils/createTestProject')

const runSilently = fn => {
  const log = console.log
  console.log = () => {}
  const res = fn()
  console.log = log
  return res
}

test('should work', async () => {
  const project = await create('eslint', {
    plugins: {
      '@vue/cli-plugin-babel': {},
      '@vue/cli-plugin-eslint': {
        config: 'airbnb',
        lintOn: 'commit'
      }
    }
  })
  const { read, write, run } = project
  // should've applied airbnb autofix
  const main = await read('src/main.js')
  expect(main).toMatch(';')
  // remove semicolons
  const updatedMain = main.replace(/;/g, '')
  await write('src/main.js', updatedMain)
  // lint
  await run('vue-cli-service lint')
  expect(await read('src/main.js')).toMatch(';')

  // lint-on-commit
  await runSilently(() => {
    require('yorkie/src/install')(path.join(project.dir, 'node_modules'))
    // since yorkie isn't actually installed in the test project, we need to
    // symlink it
    return linkBin(
      path.resolve(require.resolve('yorkie/src/install'), '../../'),
      path.join(project.dir, 'node_modules', 'yorkie')
    )
  })
  const hook = await read('.git/hooks/pre-commit')
  expect(hook).toMatch('#yorkie')
  await write('src/main.js', updatedMain)
  // nvm doesn't like PREFIX env
  if (process.platform === 'darwin') {
    delete process.env.PREFIX
  }
  await run('git add -A')
  await run('git commit -m save')
  // should be linted on commit
  expect(await read('src/main.js')).toMatch(';')

  // lint-on-save needs to be tested in a callback
  let done
  const donePromise = new Promise(resolve => {
    done = resolve
  })
  // enable lintOnSave
  await write('vue.config.js', 'module.exports = { lintOnSave: true }')
  // write invalid file
  const app = await read('src/App.vue')
  const updatedApp = app.replace(/;/g, '')
  await write('src/App.vue', updatedApp)

  const server = run('vue-cli-service serve')

  let isFirstMsg = true
  server.stdout.on('data', data => {
    data = data.toString()
    if (data.match(/Failed to compile/)) {
      // should fail on start
      expect(isFirstMsg).toBe(true)
      isFirstMsg = false
      // fix it
      write('src/App.vue', app)
    } else if (data.match(/Compiled successfully/)) {
      // should compile on 2nd update
      expect(isFirstMsg).toBe(false)
      server.stdin.write('close')
      done()
    }
  })

  await donePromise
})


keep keep keep keep replace replace replace keep keep keep keep keep

const generateWithPlugin = require('@vue/cli-test-utils/generateWithPlugin')

test('generate files', async () => {
  const { files } = await generateWithPlugin([
    {
      id: 'core',
      apply: require('@vue/cli-service/generator'),
      options: {}
    },
    {
      id: 'ts',
      apply: require('../generator'),
      options: {}
    }
  ])

  expect(files['src/main.ts']).toBeTruthy()
  expect(files['src/main.js']).toBeFalsy()
  expect(files['src/App.vue']).toMatch('<script lang="ts">')
})

test('classComponent', async () => {
  const { pkg, files } = await generateWithPlugin([
    {
      id: 'ts',
      apply: require('../generator'),
      options: {
        classComponent: true
      }
    }
  ])

  expect(pkg.devDependencies).toHaveProperty('vue-class-component')
  expect(pkg.devDependencies).toHaveProperty('vue-property-decorator')

  expect(files['tsconfig.json']).toMatch(`"experimentalDecorators": true`)
  expect(files['tsconfig.json']).toMatch(`"emitDecoratorMetadata": true`)
  expect(files['src/App.vue']).toMatch(`export default class App extends Vue {`)
  expect(files['src/components/HelloWorld.vue']).toMatch(`export default class HelloWorld extends Vue {`)
})

test('use with Babel', async () => {
  const { pkg, files } = await generateWithPlugin([
    {
      id: 'babel',
      apply: require('@vue/cli-plugin-babel/generator'),
      options: {}
    },
    {
      id: 'ts',
      apply: require('../generator'),
      options: {
        useTsWithBabel: true
      }
    }
  ])

  expect(pkg.devDependencies).toHaveProperty('@vue/babel-preset-app')
  expect(pkg.babel).toEqual({ presets: ['@vue/app'] })
  expect(files['tsconfig.json']).toMatch(`"target": "es2015"`)
})

test('lint', async () => {
  const { pkg, files } = await generateWithPlugin([
    {
      id: 'ts',
      apply: require('../generator'),
      options: {
        lint: true,
        lintOn: ['save', 'commit']
      }
    }
  ])

  expect(pkg.scripts.lint).toBe(`vue-cli-service lint`)
  expect(pkg.vue).toEqual({ lintOnSave: true })
  expect(pkg.devDependencies).toHaveProperty('lint-staged')
  expect(pkg.gitHooks).toEqual({ 'pre-commit': 'lint-staged' })
  expect(pkg['lint-staged']).toEqual({
    '*.ts': ['vue-cli-service lint', 'git add'],
    '*.vue': ['vue-cli-service lint', 'git add']
  })

  expect(files['tslint.json']).toBeTruthy()
})

test('compat with unit-mocha', async () => {
  const { pkg } = await generateWithPlugin([
    {
      id: '@vue/cli-plugin-unit-mocha',
      apply: () => {},
      options: {}
    },
    {
      id: 'ts',
      apply: require('../generator'),
      options: {
        lint: true,
        lintOn: ['save', 'commit']
      }
    }
  ])

  expect(pkg.devDependencies).toHaveProperty('@types/mocha')
  expect(pkg.devDependencies).toHaveProperty('@types/chai')
})

test('compat with unit-jest', async () => {
  const { pkg } = await generateWithPlugin([
    {
      id: '@vue/cli-plugin-unit-jest',
      apply: () => {},
      options: {}
    },
    {
      id: 'ts',
      apply: require('../generator'),
      options: {
        lint: true,
        lintOn: ['save', 'commit']
      }
    }
  ])

  expect(pkg.devDependencies).toHaveProperty('@types/jest')
})


jest.setTimeout(30000)

const path = require('path')
const portfinder = require('portfinder')
const { createServer } = require('http-server')
const { defaults } = require('@vue/cli/lib/options')
const create = require('@vue/cli-test-utils/createTestProject')
const launchPuppeteer = require('@vue/cli-test-utils/launchPuppeteer')

let server, browser, page
test('build', async () => {
  const project = await create('e2e-build', defaults)

  // test public copy
  project.write('public/foo.js', '1')

  const { stdout } = await project.run('vue-cli-service build')
  expect(stdout).toMatch('Build complete.')

  expect(project.has('dist/index.html')).toBe(true)
  expect(project.has('dist/favicon.ico')).toBe(true)
  expect(project.has('dist/js')).toBe(true)
  expect(project.has('dist/css')).toBe(true)
  expect(project.has('dist/foo.js')).toBe(true)

  const index = await project.read('dist/index.html')
  // should split and preload app.js & vendor.js
  expect(index).toMatch(/<link rel=preload [^>]+app[^>]+\.js>/)
  expect(index).toMatch(/<link rel=preload [^>]+vendor[^>]+\.js>/)
  // should not preload manifest because it's inlined
  expect(index).not.toMatch(/<link rel=preload [^>]+manifest[^>]+\.js>/)
  // should inline manifest and wepback runtime
  expect(index).toMatch('webpackJsonp')

  const port = await portfinder.getPortPromise()
  server = createServer({ root: path.join(project.dir, 'dist') })

  await new Promise((resolve, reject) => {
    server.listen(port, err => {
      if (err) return reject(err)
      resolve()
    })
  })

  const launched = await launchPuppeteer(`http://localhost:${port}/`)
  browser = launched.browser
  page = launched.page

  const h1Text = await page.evaluate(() => {
    return document.querySelector('h1').textContent
  })

  expect(h1Text).toMatch('Welcome to Your Vue.js App')
})

afterAll(async () => {
  await browser.close()
  server.close()
})


module.exports = (api, options) => {
  api.chainWebpack(webpackConfig => {
    if (process.env.NODE_ENV === 'production') {
      webpackConfig
        .devtool('source-map')
        .output
          .filename(`js/[name].[chunkhash:8].js`)
          .chunkFilename(`js/[id].[chunkhash:8].js`)

      // keep module.id stable when vendor modules does not change
      webpackConfig
        .plugin('hash-module-ids')
          .use(require('webpack/lib/HashedModuleIdsPlugin'))

      // enable scope hoisting / tree shaking
      webpackConfig
        .plugin('module-concatenation')
          .use(require('webpack/lib/optimize/ModuleConcatenationPlugin'))

      // minify HTML
      webpackConfig
        .plugin('html')
          .tap(([options]) => [Object.assign(options, {
            minify: {
              removeComments: true,
              collapseWhitespace: true,
              removeAttributeQuotes: true
              // more options:
              // https://github.com/kangax/html-minifier#options-quick-reference
            },
            // necessary to consistently work with multiple chunks via CommonsChunkPlugin
            chunksSortMode: 'dependency'
          })])

      // optimize CSS (dedupe)
      webpackConfig
        .plugin('optimize-css')
          .use(require('optimize-css-assets-webpack-plugin'), [
            options.productionSourceMap && options.cssSourceMap
              ? { safe: true, map: { inline: false }}
              : { safe: true }
          ])

      // minify JS
      const UglifyPlugin = require('uglifyjs-webpack-plugin')
      const uglifyPluginOptions = {
        uglifyOptions: {
          compress: {
            // turn off flags with small gains to speed up minification
            arrows: false,
            collapse_vars: false, // 0.3kb
            comparisons: false,
            computed_props: false,
            hoist_funs: false,
            hoist_props: false,
            hoist_vars: false,
            inline: false,
            loops: false,
            negate_iife: false,
            properties: false,
            reduce_funcs: false,
            reduce_vars: false,
            switches: false,
            toplevel: false,
            typeofs: false,

            // a few flags with noticable gains/speed ratio
            // numbers based on out of the box vendor bundle
            booleans: true, // 0.7kb
            if_return: true, // 0.4kb
            sequences: true, // 0.7kb
            unused: true, // 2.3kb

            // required features to drop conditional branches
            conditionals: true,
            dead_code: true,
            evaluate: true
          }
        },
        sourceMap: options.productionSourceMap,
        cache: true,
        parallel: true
      }
      // disable during tests to speed things up
      if (!process.env.VUE_CLI_TEST) {
        webpackConfig
          .plugin('uglify')
            .use(UglifyPlugin, [uglifyPluginOptions])
      }

      const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin')

      // Chunk splits
      if (!options.dll) {
        // extract vendor libs into its own chunk for better caching, since they
        // are more likely to stay the same.
        webpackConfig
          .plugin('split-vendor')
            .use(CommonsChunkPlugin, [{
              name: 'vendor',
              minChunks (module) {
                // any required modules inside node_modules are extracted to vendor
                return (
                  module.resource &&
                  /\.js$/.test(module.resource) &&
                  module.resource.indexOf(`node_modules`) > -1
                )
              }
            }])

        // extract webpack runtime and module manifest to its own file in order to
        // prevent vendor hash from being updated whenever app bundle is updated
        webpackConfig
          .plugin('split-manifest')
            .use(CommonsChunkPlugin, [{
              name: 'manifest',
              minChunks: Infinity
            }])

        // inline the manifest chunk into HTML
        webpackConfig
          .plugin('inline-manifest')
            .use(require('../webpack/InlineSourcePlugin'), [{
              include: /manifest\..*\.js$/
            }])

        // since manifest is inlined, don't preload it anymore
        webpackConfig
          .plugin('preload')
            .tap(([options]) => {
              options.fileBlacklist.push(/manifest\..*\.js$/)
              return [options]
            })
      }

      // This CommonsChunkPlugin instance extracts shared chunks from async
      // chunks and bundles them in a separate chunk, similar to the vendor chunk
      // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
      webpackConfig
        .plugin('split-vendor-async')
          .use(CommonsChunkPlugin, [{
            name: 'app',
            async: 'vendor-async',
            children: true,
            minChunks: 3
          }])

      // DLL
      if (options.dll) {
        const webpack = require('webpack')
        const resolveClientEnv = require('../util/resolveClientEnv')
        const dllEntries = Array.isArray(options.dll)
          ? options.dll
          : Object.keys(api.service.pkg.dependencies)

        webpackConfig
          .plugin('dll')
            .use(require('autodll-webpack-plugin'), [{
              inject: true,
              inherit: true,
              path: 'js/',
              context: api.resolve('.'),
              filename: '[name].[hash:8].js',
              entry: {
                'vendor': [
                  ...dllEntries,
                  'vue-loader/lib/component-normalizer'
                ]
              },
              plugins: [
                new webpack.DefinePlugin(resolveClientEnv(options.baseUrl)),
                new UglifyPlugin(uglifyPluginOptions)
              ]
            }])
            .after('preload')
      }

      // copy static assets in public/
      webpackConfig
        .plugin('copy')
          .use(require('copy-webpack-plugin'), [[{
            from: api.resolve('public'),
            to: api.resolve(options.outputDir),
            ignore: ['index.html', '.*']
          }]])

      // TODO parallelazation
      // thread-loader doesn't seem to have obvious effect because vue-loader
      // offloads most of the work to other loaders. We may need to re-think
      // vue-loader implementation in order to better take advantage of
      // parallelazation

      // webpackConfig.module
      //   .rule('vue')
      //     .use('thread-loader')
      //       .before('vue-loader')
      //       .loader('thread-loader')
      //       .options({ name: 'vue' })
    }
  })
}


#!/usr/bin/env node

const fs = require('fs')
const path = require('path')
const slash = require('slash')
const chalk = require('chalk')
const semver = require('semver')
const minimist = require('minimist')
const { error } = require('@vue/cli-shared-utils')
const requiredVersion = require('../package.json').engines.node

if (!semver.satisfies(process.version, requiredVersion)) {
  error(
    `You are using Node ${process.version}, but this version of vue-cli ` +
    `requires Node ${requiredVersion}.\nPlease upgrade your Node version.`
  )
  process.exit(1)
}

// enter debug mode when creating test repo
if (
  slash(process.cwd()).indexOf('/packages/test') > 0 && (
    fs.existsSync(path.resolve(process.cwd(), '../@vue')) ||
    fs.existsSync(path.resolve(process.cwd(), '../../@vue'))
  )
) {
  process.env.VUE_CLI_DEBUG = true
}

const program = require('commander')
const loadCommand = require('../lib/util/loadCommand')

program
  .version(require('../package').version)
  .usage('<command> [options]')

program
  .command('create <app-name>')
  .description('create a new project powered by vue-cli-service')
  .option('-s, --saved', 'Skip prompts and use saved config')
  .option('-d, --default', 'Skip prompts and use default config')
  .option('-c, --config <json>', 'Skip prompts and use inline JSON string as config')
  .option('-r, --registry <url>', 'Use specified NPM registry when installing dependencies')
  .option('-m, --packageManager <command>', 'Use specified NPM client when installing dependencies')
  .option('-f, --force', 'Overwrite target directory if it exists')
  .action((name, cmd) => {
    require('../lib/create')(name, cleanArgs(cmd))
  })

program
  .command('invoke <plugin>')
  .allowUnknownOption()
  .description('invoke the generator of a plugin in an already created project')
  .action((plugin) => {
    require('../lib/invoke')(plugin, minimist(process.argv.slice(3)))
  })

program
  .command('serve [entry]')
  .description('serve a .js or vue file in development mode with zero config')
  .option('-o, --open', 'Open browser')
  .action((entry, cmd) => {
    loadCommand('serve', '@vue/cli-service-global').serve(entry, cleanArgs(cmd))
  })

program
  .command('build [entry]')
  .option('-t, --target', 'Build target (app, lib, web-component). Default: app')
  .option('-f, --format', 'How the lib is exposed (iife, amd, cjs, umd). Default: umd')
  .option('-n, --name', 'Library name for umd export')
  .description('build a .js or .vue file in production mode with zero config')
  .action((entry, cmd) => {
    loadCommand('build', '@vue/cli-service-global').build(entry, cleanArgs(cmd))
  })

program
  .command('init <template> <app-name>')
  .description('generate a project from a remote template (legacy API, requires @vue/cli-init)')
  .action(() => {
    loadCommand('init', '@vue/cli-init')
  })

// add some useful info on help
program.on('--help', () => {
  console.log()
  console.log(`  Run ${chalk.cyan(`vue <command> --help`)} for detailed usage of given command.`)
  console.log()
})

program.commands.forEach(c => c.on('--help', () => console.log()))

// enhance common error messages
const enhanceErrorMessages = (methodName, log) => {
  program.Command.prototype[methodName] = function (...args) {
    if (methodName === 'unknownOption' && this._allowUnknownOption) {
      return
    }
    this.outputHelp()
    console.log(`  ` + chalk.red(log(...args)))
    console.log()
    process.exit(1)
  }
}

enhanceErrorMessages('missingArgument', argName => {
  return `Missing required argument ${chalk.yellow(`<${argName}>`)}.`
})

enhanceErrorMessages('unknownOption', optionName => {
  return `Unknown option ${chalk.yellow(optionName)}.`
})

enhanceErrorMessages('optionMissingArgument', (option, flag) => {
  return `Missing required argument for option ${chalk.yellow(option.flags)}` + (
    flag ? `, got ${chalk.yellow(flag)}` : ``
  )
})

program.parse(process.argv)

if (!process.argv.slice(2).length) {
  program.outputHelp()
}

// commander passes the Command object itself as options,
// extract only actual options into a fresh object.
function cleanArgs (cmd) {
  const args = {}
  cmd.options.forEach(o => {
    const key = o.long.replace(/^--/, '')
    args[key] = cmd[key]
  })
  return args
}


},
"dependencies": {
"babel-eslint": "^8.2.1",
"eslint": "^4.16.0",
"eslint-loader": "^1.9.0"
},
"devDependencies": {
"eslint-plugin-vue": "^4.0.0"
}
}

keep keep keep keep replace replace replace keep keep keep


# @vue/eslint-config-standard

> eslint-config-standard for vue-cli


const path = require('path')
const chalk = require('chalk')
const debug = require('debug')
const execa = require('execa')
const resolve = require('resolve')
const inquirer = require('inquirer')
const Generator = require('./Generator')
const cloneDeep = require('lodash.clonedeep')
const sortObject = require('./util/sortObject')
const installDeps = require('./util/installDeps')
const clearConsole = require('./util/clearConsole')
const PromptModuleAPI = require('./PromptModuleAPI')
const writeFileTree = require('./util/writeFileTree')
const formatFeatures = require('./util/formatFeatures')
const setupDevProject = require('./util/setupDevProject')

const {
  defaults,
  validate,
  saveOptions,
  loadOptions
} = require('./options')

const {
  log,
  error,
  hasGit,
  hasYarn,
  logWithSpinner,
  stopSpinner
} = require('@vue/cli-shared-utils')

const isMode = _mode => ({ mode }) => _mode === mode

module.exports = class Creator {
  constructor (name, context, promptModules) {
    this.name = name
    this.context = process.env.VUE_CLI_CONTEXT = context
    const { modePrompt, featurePrompt } = this.resolveIntroPrompts()
    this.modePrompt = modePrompt
    this.featurePrompt = featurePrompt
    this.outroPrompts = this.resolveOutroPrompts()
    this.injectedPrompts = []
    this.promptCompleteCbs = []
    this.createCompleteCbs = []

    const promptAPI = new PromptModuleAPI(this)
    promptModules.forEach(m => m(promptAPI))
  }

  async create (cliOptions = {}) {
    const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG
    const { name, context, createCompleteCbs } = this
    const run = (command, args) => {
      if (!args) { [command, ...args] = command.split(/\s+/) }
      return execa(command, args, { cwd: context })
    }

    let options
    if (cliOptions.saved) {
      options = loadOptions()
    } else if (cliOptions.default) {
      options = defaults
    } else if (cliOptions.config) {
      try {
        options = JSON.parse(cliOptions.config)
      } catch (e) {
        error(`CLI inline config is not valid JSON: ${cliOptions.config}`)
        process.exit(1)
      }
    } else {
      options = await this.promptAndResolveOptions()
    }

    // clone before mutating
    options = cloneDeep(options)
    // inject core service
    options.plugins['@vue/cli-service'] = Object.assign({
      projectName: name
    }, options)

    const packageManager = (
      cliOptions.packageManager ||
      options.packageManager ||
      (hasYarn ? 'yarn' : 'npm')
    )

    // write base package.json to disk
    clearConsole()
    logWithSpinner('✨', `Creating project in ${chalk.yellow(context)}.`)
    await writeFileTree(context, {
      'package.json': JSON.stringify({
        name,
        version: '0.1.0',
        private: true
      }, null, 2)
    })

    // intilaize git repository
    if (hasGit) {
      logWithSpinner('🗃', `Initializing git repository...`)
      await run('git init')
    }

    // install plugins
    logWithSpinner('⚙', `Installing CLI plugins. This might take a while...`)
    const deps = Object.keys(options.plugins)
    if (isTestOrDebug) {
      // in development, avoid installation process
      await setupDevProject(context, deps)
    } else {
      await installDeps(context, packageManager, deps, cliOptions.registry)
    }

    // run generator
    logWithSpinner('🚀', `Invoking generators...`)
    const pkg = require(path.join(context, 'package.json'))
    const plugins = this.resolvePlugins(options.plugins)
    const generator = new Generator(
      context,
      pkg,
      plugins,
      createCompleteCbs
    )
    await generator.generate()

    // install additional deps (injected by generators)
    logWithSpinner('📦', `Installing additional dependencies...`)
    if (!isTestOrDebug) {
      await installDeps(context, packageManager, null, cliOptions.registry)
    }

    // run complete cbs if any (injected by generators)
    logWithSpinner('⚓', `Running completion hooks...`)
    for (const cb of createCompleteCbs) {
      await cb()
    }

    // commit initial state
    if (hasGit) {
      await run('git add -A')
      if (isTestOrDebug) {
        await run('git', ['config', 'user.name', 'test'])
        await run('git', ['config', 'user.email', 'test@test.com'])
      }
      await run(`git commit -m init`)
    }

    // log instructions
    stopSpinner()
    log()
    log(`🎉  Successfully created project ${chalk.yellow(name)}.`)
    log(
      `👉  Get started with the following commands:\n\n` +
      chalk.cyan(` ${chalk.gray('$')} cd ${name}\n`) +
      chalk.cyan(` ${chalk.gray('$')} ${options.packageManager === 'yarn' ? 'yarn serve' : 'npm run serve'}`)
    )
    log()
  }

  async promptAndResolveOptions () {
    // prompt
    clearConsole()
    const answers = await inquirer.prompt(this.resolveFinalPrompts())
    debug('vue:cli-answers')(answers)

    let options
    if (answers.mode === 'saved') {
      options = loadOptions()
    } else if (answers.mode === 'default') {
      options = defaults
    } else {
      // manual
      options = {
        packageManager: answers.packageManager || loadOptions().packageManager,
        plugins: {}
      }
      // run cb registered by prompt modules to finalize the options
      this.promptCompleteCbs.forEach(cb => cb(answers, options))
    }

    // validate
    validate(options)

    // save options
    if (answers.mode === 'manual' && answers.save) {
      saveOptions(options, true /* replace */)
    }

    debug('vue:cli-options')(options)
    return options
  }

  // { id: options } => [{ id, apply, options }]
  resolvePlugins (rawPlugins) {
    // ensure cli-service is invoked first
    rawPlugins = sortObject(rawPlugins, ['@vue/cli-service'])
    return Object.keys(rawPlugins).map(id => {
      const module = resolve.sync(`${id}/generator`, { basedir: this.context })
      return {
        id,
        apply: require(module),
        options: rawPlugins[id]
      }
    })
  }

  resolveIntroPrompts () {
    const defualtFeatures = formatFeatures(defaults)
    const modePrompt = {
      name: 'mode',
      type: 'list',
      message: `Please pick a project creation mode:`,
      choices: [
        {
          name: `Zero-config with defaults (${defualtFeatures})`,
          value: 'default'
        },
        {
          name: 'Manually select features',
          value: 'manual'
        }
      ]
    }
    const savedOptions = loadOptions()
    if (savedOptions.plugins) {
      const savedFeatures = formatFeatures(savedOptions)
      modePrompt.choices.unshift({
        name: `Use previously saved config (${savedFeatures})`,
        value: 'saved'
      })
    }
    const featurePrompt = {
      name: 'features',
      when: isMode('manual'),
      type: 'checkbox',
      message: 'Check the features needed for your project:',
      choices: [],
      pageSize: 8
    }
    return {
      modePrompt,
      featurePrompt
    }
  }

  resolveOutroPrompts () {
    const outroPrompts = []
    const savedOptions = loadOptions()
    if (hasYarn && !savedOptions.packageManager) {
      outroPrompts.push({
        name: 'packageManager',
        when: isMode('manual'),
        type: 'list',
        message: 'Pick the package manager to use when installing dependencies:',
        choices: [
          {
            name: 'Use Yarn',
            value: 'yarn',
            short: 'Yarn'
          },
          {
            name: 'Use NPM',
            value: 'npm',
            short: 'NPM'
          }
        ]
      })
    }
    outroPrompts.push({
      name: 'save',
      when: isMode('manual'),
      type: 'confirm',
      message: 'Save the preferences for future projects? (You can always manually edit ~/.vuerc)'
    })
    return outroPrompts
  }

  resolveFinalPrompts () {
    // patch generator-injected prompts to only show when mode === 'manual'
    this.injectedPrompts.forEach(prompt => {
      const originalWhen = prompt.when || (() => true)
      prompt.when = options => {
        return options.mode === 'manual' && originalWhen(options)
      }
    })
    const prompts = [].concat(
      this.modePrompt,
      this.featurePrompt,
      this.injectedPrompts,
      this.outroPrompts
    )
    debug('vue:cli-prompts')(prompts)
    return prompts
  }
}


expect(pkg.babel).toEqual({ presets: ['@vue/app'] })

typescript plugin for vue-cli


const puppeteer = require('puppeteer')

const puppeteerOptions = process.env.CI
  ? { args: ['--no-sandbox', '--disable-setuid-sandbox'] }
  : {}

module.exports = async function launchPuppeteer (url) {
  const browser = await puppeteer.launch(puppeteerOptions)
  const page = await browser.newPage()

  const logs = []
  page.on('console', msg => logs.push(msg.text))

  await page.goto(url)

  return { browser, page, logs }
}


# @vue/cli

## Usage

``` sh
npm install -g @vue/cli
vue create my-app
```


keep keep keep keep replace keep keep keep keep

typescript plugin for vue-cli


jest.mock('fs')

const fs = require('fs')
const path = require('path')
const Generator = require('../lib/Generator')
const { logs } = require('@vue/cli-shared-utils')

// prepare template fixtures
const mkdirp = require('mkdirp')
const templateDir = path.resolve(__dirname, 'template')
mkdirp.sync(templateDir)
fs.writeFileSync(path.resolve(templateDir, 'foo.js'), 'foo(<%- options.n %>)')
mkdirp.sync(path.resolve(templateDir, 'bar'))
fs.writeFileSync(path.resolve(templateDir, 'bar/bar.js'), 'bar(<%- m %>)')

test('api: extendPackage', async () => {
  const generator = new Generator('/', {
    name: 'hello',
    list: [1],
    vue: {
      foo: 1,
      bar: 2
    }
  }, [{
    id: 'test',
    apply: api => {
      api.extendPackage({
        name: 'hello2',
        list: [2],
        vue: {
          foo: 2,
          baz: 3
        }
      })
    }
  }])

  await generator.generate()

  const pkg = JSON.parse(fs.readFileSync('/package.json', 'utf-8'))
  expect(pkg).toEqual({
    name: 'hello2',
    list: [1, 2],
    vue: {
      foo: 2,
      bar: 2,
      baz: 3
    }
  })
})

test('api: extendPackage function', async () => {
  const generator = new Generator('/', { foo: 1 }, [{
    id: 'test',
    apply: api => {
      api.extendPackage(pkg => ({
        foo: pkg.foo + 1
      }))
    }
  }])

  await generator.generate()

  const pkg = JSON.parse(fs.readFileSync('/package.json', 'utf-8'))
  expect(pkg).toEqual({
    foo: 2
  })
})

test('api: extendPackage + { merge: false }', async () => {
  const generator = new Generator('/', {
    name: 'hello',
    list: [1],
    vue: {
      foo: 1,
      bar: 2
    }
  }, [{
    id: 'test',
    apply: api => {
      api.extendPackage({
        name: 'hello2',
        list: [2],
        vue: {
          foo: 2,
          baz: 3
        }
      }, { merge: false })
    }
  }])

  await generator.generate()

  const pkg = JSON.parse(fs.readFileSync('/package.json', 'utf-8'))
  expect(pkg).toEqual({
    name: 'hello2',
    list: [2],
    vue: {
      foo: 2,
      baz: 3
    }
  })
})

test('api: extendPackage merge dependencies', async () => {
  const generator = new Generator('/', {}, [
    {
      id: 'test1',
      apply: api => {
        api.extendPackage({
          dependencies: {
            foo: '^1.1.0',
            bar: '^1.0.0'
          }
        })
      }
    },
    {
      id: 'test2',
      apply: api => {
        api.extendPackage({
          dependencies: {
            foo: '^1.0.0',
            baz: '^1.0.0'
          }
        })
      }
    }
  ])

  await generator.generate()

  const pkg = JSON.parse(fs.readFileSync('/package.json', 'utf-8'))
  expect(pkg).toEqual({
    dependencies: {
      foo: '^1.1.0',
      bar: '^1.0.0',
      baz: '^1.0.0'
    }
  })
})

test('api: warn invalid dep range', async () => {
  new Generator('/', {}, [
    {
      id: 'test1',
      apply: api => {
        api.extendPackage({
          dependencies: {
            foo: 'foo'
          }
        })
      }
    }
  ])

  expect(logs.warn.some(([msg]) => {
    return (
      msg.match(/invalid version range for dependency "foo"/) &&
      msg.match(/injected by generator "test1"/)
    )
  })).toBe(true)
})

test('api: extendPackage dependencies conflict', async () => {
  new Generator('/', {}, [
    {
      id: 'test1',
      apply: api => {
        api.extendPackage({
          dependencies: {
            foo: '^1.0.0'
          }
        })
      }
    },
    {
      id: 'test2',
      apply: api => {
        api.extendPackage({
          dependencies: {
            foo: '^2.0.0'
          }
        })
      }
    }
  ])

  expect(logs.warn.some(([msg]) => {
    return (
      msg.match(/conflicting versions for project dependency "foo"/) &&
      msg.match(/\^1\.0\.0 injected by generator "test1"/) &&
      msg.match(/\^2\.0\.0 injected by generator "test2"/) &&
      msg.match(/Using newer version \(\^2\.0\.0\)/)
    )
  })).toBe(true)
})

test('api: render fs directory', async () => {
  const generator = new Generator('/', {}, [
    {
      id: 'test1',
      apply: api => {
        api.render('./template', { m: 2 })
      },
      options: {
        n: 1
      }
    }
  ])

  await generator.generate()

  expect(fs.readFileSync('/foo.js', 'utf-8')).toMatch('foo(1)')
  expect(fs.readFileSync('/bar/bar.js', 'utf-8')).toMatch('bar(2)')
})

test('api: render object', async () => {
  const generator = new Generator('/', {}, [
    {
      id: 'test1',
      apply: api => {
        api.render({
          'foo1.js': path.join(templateDir, 'foo.js'),
          'bar/bar1.js': path.join(templateDir, 'bar/bar.js')
        }, { m: 3 })
      },
      options: {
        n: 2
      }
    }
  ])

  await generator.generate()

  expect(fs.readFileSync('/foo1.js', 'utf-8')).toMatch('foo(2)')
  expect(fs.readFileSync('/bar/bar1.js', 'utf-8')).toMatch('bar(3)')
})

test('api: render middleware', async () => {
  const generator = new Generator('/', {}, [
    {
      id: 'test1',
      apply: (api, options) => {
        api.render((files, render) => {
          files['foo2.js'] = render('foo(<%- n %>)', options)
          files['bar/bar2.js'] = render('bar(<%- n %>)', options)
        })
      },
      options: {
        n: 3
      }
    }
  ])

  await generator.generate()

  expect(fs.readFileSync('/foo2.js', 'utf-8')).toMatch('foo(3)')
  expect(fs.readFileSync('/bar/bar2.js', 'utf-8')).toMatch('bar(3)')
})

test('api: hasPlugin', () => {
  new Generator('/', {}, [
    {
      id: 'foo',
      apply: api => {
        expect(api.hasPlugin('foo')).toBe(true)
        expect(api.hasPlugin('bar')).toBe(true)
        expect(api.hasPlugin('baz')).toBe(true)
        expect(api.hasPlugin('vue-cli-plugin-bar')).toBe(true)
        expect(api.hasPlugin('@vue/cli-plugin-baz')).toBe(true)
      }
    },
    {
      id: 'vue-cli-plugin-bar',
      apply: () => {}
    },
    {
      id: '@vue/cli-plugin-baz',
      apply: () => {}
    }
  ])
})

test('api: onCreateComplete', () => {
  const fn = () => {}
  const cbs = []
  new Generator('/', {}, [
    {
      id: 'test',
      apply: api => {
        api.onCreateComplete(fn)
      }
    }
  ], cbs)
  expect(cbs).toContain(fn)
})

test('api: resolve', () => {
  new Generator('/foo/bar', {}, [
    {
      id: 'test',
      apply: api => {
        expect(api.resolve('baz')).toBe(path.resolve('/foo/bar', 'baz'))
      }
    }
  ])
})


# @vue/cli-shared-utils

> shared-utils for vue-cli


const path = require('path')

module.exports = class PluginAPI {
  constructor (id, service) {
    this.id = id
    this.service = service
  }

  resolve (_path) {
    return path.resolve(this.service.context, _path)
  }

  hasPlugin (id) {
    const prefixRE = /^(@vue\/|vue-)cli-plugin-/
    return this.service.plugins.some(p => {
      return p.id === id || p.id.replace(prefixRE, '') === id
    })
  }

  // set project mode.
  // this should be called by any registered command as early as possible.
  setMode (mode) {
    process.env.VUE_CLI_MODE = mode
    // by default, NODE_ENV and BABEL_ENV are set to "development" unless mode
    // is production or test. However this can be overwritten in .env files.
    process.env.NODE_ENV = process.env.BABEL_ENV =
      (mode === 'production' || mode === 'test')
        ? mode
        : 'development'
    // load .env files based on mode
    this.service.loadEnv(mode)
  }

  registerCommand (name, opts, fn) {
    if (typeof opts === 'function') {
      fn = opts
      opts = null
    }
    this.service.commands[name] = { fn, opts }
  }

  chainWebpack (fn) {
    this.service.webpackChainFns.push(fn)
  }

  configureWebpack (fn) {
    this.service.webpackRawConfigFns.push(fn)
  }

  resolveWebpackConfig () {
    return this.service.resolveWebpackConfig()
  }

  configureDevServer (fn) {
    this.service.devServerConfigFns.push(fn)
  }
}


keep keep keep keep add keep keep keep keep keep

@vue/cli-service

local service for vue-cli projects


jest.mock('fs')
jest.mock('inquirer')

const { defaults } = require('../lib/options')
const assertPromptModule = require('@vue/cli-test-utils/assertPromptModule')

test('default', async () => {
  const epxectedPrompts = [
    {
      message: 'project creation mode',
      choices: [
        'Zero-config',
        'Manually select'
      ],
      choose: 0
    }
  ]
  assertPromptModule([], epxectedPrompts, defaults)
})

test('manual + PromptModuleAPI', async () => {
  const mockModule = api => {
    api.injectFeature({
      name: 'Foo',
      value: 'foo'
    })
    api.injectFeature({
      name: 'Bar',
      value: 'bar'
    })
    api.injectPrompt({
      name: 'customFoo',
      message: 'customFoo',
      when: answers => answers.features.includes('foo'),
      type: 'confirm'
    })
    api.injectPrompt({
      name: 'customBar',
      message: 'customBar',
      when: answers => answers.features.includes('bar'),
      type: 'list',
      choices: []
    })
    api.injectOptionForPrompt('customBar', {
      name: 'barChoice',
      value: 'barChoice'
    })
    api.onPromptComplete((answers, options) => {
      if (answers.features.includes('bar')) {
        options.plugins.bar = {}
      }
      if (answers.customBar === 'barChoice') {
        options.plugins.barChoice = {}
      }
    })
  }

  const expectedPrompts = [
    { choose: 1 },
    {
      message: 'Check the features',
      choices: ['Foo', 'Bar'],
      check: [1]
    },
    {
      message: 'customBar',
      choices: ['barChoice'],
      choose: 0
    },
    {
      message: 'package manager',
      choices: ['Yarn', 'NPM'],
      choose: 0
    },
    {
      message: 'Save the preferences',
      confirm: true
    }
  ]

  const expectedOptions = {
    packageManager: 'yarn',
    plugins: {
      bar: {},
      barChoice: {}
    }
  }

  await assertPromptModule(mockModule, expectedPrompts, expectedOptions)

  // should be saved now
  const expectedPromptsForSaved = [
    {
      choices: [
        'Use previously saved',
        'Zero-config',
        'Manually'
      ],
      choose: 0
    }
  ]
  await assertPromptModule([], expectedPromptsForSaved, expectedOptions)
})


import Vue from 'vue'
import App from '~entry'

Vue.config.productionTip = false

new Vue({ render: h => h(App) }).$mount('#app')


module.exports = {
  extends: [
    require.resolve('eslint-config-airbnb-base')
  ],
  settings: {
    'import/resolver': {
      webpack: {
        config: require.resolve('@vue/cli-service/webpack.config.js')
      }
    }
  }
}


const cliBinPath = require.resolve('../bin/vue')

await project.run(`${cliBinPath} invoke eslint --config airbnb --lintOn save,commit`)

const updatedPkg = JSON.parse(await project.read('package.json'))

keep keep keep keep keep replace keep keep keep

@vue/cli-plugin-babel

babel plugin for vue-cli


module.exports = {
  plugins: ['prettier'],
  extends: [
    require.resolve('eslint-config-prettier')
  ],
  rules: {
    'prettier/prettier': 'warn'
  }
}


// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')


import { shallow } from '@vue/test-utils'
import HelloWorld from '@/components/HelloWorld.vue'

describe('Hello.vue', () => {
  it('renders props.msg when passed', () => {
    const msg = 'new message'
    const wrapper = shallow(HelloWorld, {
      propsData: { msg }
    })
    expect(wrapper.text()).toMatch(msg)
  })
})


import { options, createElement as h, render } from 'preact';
import { useEffect, useState } from 'preact/hooks';

import { setupScratch, teardown } from '../../../test/_util/helpers';
import { act } from '../../src';

/** @jsx h */
describe('act', () => {

	/** @type {HTMLDivElement} */
	let scratch;

	beforeEach(() => {
		scratch = setupScratch();
	});

	afterEach(() => {
		teardown(scratch);
	});

	it('should reset options after act finishes', () => {
		expect(options.requestAnimationFrame).to.equal(undefined);
		act(() => null);
		expect(options.requestAnimationFrame).to.equal(undefined);
	});

	it('should flush pending effects', () => {
		let spy = sinon.spy();
		function StateContainer() {
			useEffect(spy);
			return
;
		}
		act(() => render(<StateContainer />, scratch));
		expect(spy).to.be.calledOnce;
	});

	it('should flush pending and initial effects', () => {
		const spy = sinon.spy();
		function StateContainer() {
			const [count, setCount] = useState(0);
			useEffect(() => spy(), [count]);
			return (

Count: {count}

					<button onClick={() => setCount(c => c + 11)} />

			);
		}

		act(() => render(<StateContainer />, scratch));
		expect(spy).to.be.calledOnce;
		expect(scratch.textContent).to.include('Count: 0');
		act(() => {
			const button = scratch.querySelector('button');
			button.click();
			expect(spy).to.be.calledOnce;
			expect(scratch.textContent).to.include('Count: 0');
		});
		expect(spy).to.be.calledTwice;
		expect(scratch.textContent).to.include('Count: 1');
	});

	it('should flush series of hooks', () => {
		const spy = sinon.spy();
		const spy2 = sinon.spy();
		function StateContainer() {
			const [count, setCount] = useState(0);
			useEffect(() => {
				spy();
				if (count===1) {
					setCount(() => 2);
				}
			}, [count]);
			useEffect(() => {
				if (count === 2) {
					spy2();
					setCount(() => 4);
					return () => setCount(() => 3);
				}
			}, [count]);
			return (

Count: {count}

					<button onClick={() => setCount(c => c + 1)} />

			);
		}
		act(() => render(<StateContainer />, scratch));
		expect(spy).to.be.calledOnce;
		expect(scratch.textContent).to.include('Count: 0');
		act(() => {
			const button = scratch.querySelector('button');
			button.click();
		});
		expect(spy.callCount).to.equal(5);
		expect(spy2).to.be.calledOnce;
		expect(scratch.textContent).to.include('Count: 3');
	});

	it('should drain the queue of hooks', () => {
		const spy = sinon.spy();
		function StateContainer() {
			const [count, setCount] = useState(0);
			useEffect(() => spy());
			return (

Count: {count}

				<button onClick={() => setCount(c => c + 11)} />

);
		}

		render(<StateContainer />, scratch);
		expect(scratch.textContent).to.include('Count: 0');
		act(() => {
			const button = scratch.querySelector('button');
			button.click();
			expect(scratch.textContent).to.include('Count: 0');
		});
		expect(scratch.textContent).to.include('Count: 1');
	});

	it('should restore options.requestAnimationFrame', () => {
		const spy = sinon.spy();

		options.requestAnimationFrame = spy;
		act(() => null);

		expect(options.requestAnimationFrame).to.equal(spy);
		expect(spy).to.not.be.called;
	});
});


It controls protected/private property mangling so that minified builds have consistent property names.

Most properties are only used on one type of objects, so they can have the same name since they will never collide. Doing this reduces size.


import { setupScratch, teardown } from '../../../test/_util/helpers';
import { render, createElement as h } from '../../src';

/** @jsx h */

describe('Select', () => {
	let scratch;

	beforeEach(() => {
		scratch = setupScratch();
	});

	afterEach(() => {
		teardown(scratch);
	});

	it('should work with multiple selected (array of values)', () => {
		function App() {
			return (
				<select multiple value={['B', 'C']}>
					<option value="A">A</option>
					<option value="B">B</option>
					<option value="C">C</option>
				</select>
			);
		}

		render(<App />, scratch);
		Array.prototype.slice.call(scratch.firstChild.childNodes).forEach(node => {
			if (node.value === 'B' || node.value === 'C') {
				expect(node.selected).to.equal(true);
			}
		});
		expect(scratch.firstChild.value).to.equal('B');
	});
});


test-utils

Test-utils for Preact


import { options } from 'preact';

/**
 * Setup a rerender function that will drain the queue of pending renders
 * @returns {() => void}
 */
export function setupRerender() {
	options.__test__previousDebounce = options.debounceRendering;
	options.debounceRendering = cb => options.__test__drainQueue = cb;
	return () => options.__test__drainQueue && options.__test__drainQueue();
}

/**
 * Run a test function, and flush all effects and rerenders after invoking it
 * @param {() => void} cb The function under test
 */
export function act(cb) {
	const previousRequestAnimationFrame = options.requestAnimationFrame;
	const rerender = setupRerender();

	/** @type {() => void} */
	let flush, toFlush;

	// Override requestAnimationFrame so we can flush pending hooks.
	options.requestAnimationFrame = (fc) => flush = fc;

	// Execute the callback we were passed.
	cb();
	rerender();

	while (flush) {
		toFlush = flush;
		flush = null;

		toFlush();
		rerender();
	}

	options.requestAnimationFrame = previousRequestAnimationFrame;
}

/**
 * Teardown test environment and reset preact's internal state
 */
export function teardown() {
	if (options.__test__drainQueue) {
		// Flush any pending updates leftover by test
		options.__test__drainQueue();
		delete options.__test__drainQueue;
	}

	if (typeof options.__test__previousDebounce !== 'undefined') {
		options.debounceRendering = options.__test__previousDebounce;
		delete options.__test__previousDebounce;
	}
}


import { initDebug } from './debug';
import { initDevTools } from './devtools';

if (process.env.NODE_ENV==='development') {
	initDebug();
	initDevTools();
}