keep keep keep keep replace keep keep keep keep keep

module.exports = (api, options) => {
  api.render('./template', {
    doesCompile: api.hasPlugin('babel') || api.hasPlugin('typescript')
  })

  api.extendPackage({
    scripts: {
      'serve': 'vue-cli-service serve',
      'build': 'vue-cli-service build'
    },
    dependencies: {
      'vue': '^2.6.11'
    },
    devDependencies: {
      'vue-template-compiler': '^2.6.11'
    },
    browserslist: [
      '> 1%',
      'last 2 versions'
    ]
  })

  if (options.cssPreprocessor) {
    const deps = {
      sass: {
        sass: '^1.25.0',
        'sass-loader': '^8.0.2'
      },
      'node-sass': {
        'node-sass': '^4.12.0',
        'sass-loader': '^8.0.2'
      },
      'dart-sass': {
        sass: '^1.25.0',
        'sass-loader': '^8.0.2'
      },
      less: {
        'less': '^3.0.4',
        'less-loader': '^5.0.0'
      },
      stylus: {
        'stylus': '^0.54.7',
        'stylus-loader': '^3.0.2'
      }
    }

    api.extendPackage({
      devDependencies: deps[options.cssPreprocessor]
    })
  }

  // for v3 compatibility
  if (options.router && !api.hasPlugin('router')) {
    require('./router')(api, options)
  }

  // for v3 compatibility
  if (options.vuex && !api.hasPlugin('vuex')) {
    require('./vuex')(api)
  }

  // additional tooling configurations
  if (options.configs) {
    api.extendPackage(options.configs)
  }
}


jest.setTimeout(30000)

const path = require('path')
const portfinder = require('portfinder')
const createServer = require('@vue/cli-test-utils/createServer')
const { defaultPreset } = 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-cors', defaultPreset)

  await project.write('vue.config.js', `
    module.exports = {
      crossorigin: '',
      integrity: true
    }
  `)

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

  const index = await project.read('dist/index.html')

  // preload disabled due to chrome bug
  // https://bugs.chromium.org/p/chromium/issues/detail?id=677022
  // expect(index).toMatch(/<link [^>]+js\/app[^>]+\.js rel=preload as=script crossorigin>/)
  // expect(index).toMatch(/<link [^>]+js\/chunk-vendors[^>]+\.js rel=preload as=script crossorigin>/)
  // expect(index).toMatch(/<link [^>]+app[^>]+\.css rel=preload as=style crossorigin>/)

  // should apply crossorigin and add integrity to scripts and css
  expect(index).toMatch(/<script src=\/js\/chunk-vendors\.\w{8}\.js crossorigin integrity=sha384-.{64}\s?>/)
  expect(index).toMatch(/<script src=\/js\/app\.\w{8}\.js crossorigin integrity=sha384-.{64}\s?>/)
  expect(index).toMatch(/<link href=\/css\/app\.\w{8}\.css rel=stylesheet crossorigin integrity=sha384-.{64}\s?>/)

  // verify integrity is correct by actually running it
  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 () => {
  if (browser) {
    await browser.close()
  }
  if (server) {
    server.close()
  }
})


const { semver, loadModule } = require('@vue/cli-shared-utils')
const invoke = require('@vue/cli/lib/invoke')
const add = require('@vue/cli/lib/add')

const ROUTER = 'org.vue.vue-router-add'
const VUEX = 'org.vue.vuex-add'
const VUE_CONFIG_OPEN = 'org.vue.vue-config-open'

module.exports = api => {
  api.onViewOpen(({ view }) => {
    if (view.id === 'vue-project-plugins') {
      if (!api.hasPlugin('router')) {
        api.addSuggestion({
          id: ROUTER,
          type: 'action',
          label: 'org.vue.cli-service.suggestions.vue-router-add.label',
          message: 'org.vue.cli-service.suggestions.vue-router-add.message',
          link: 'https://router.vuejs.org/',
          async handler () {
            await install(api, 'router')
          }
        })
      }

      if (!api.hasPlugin('vuex')) {
        api.addSuggestion({
          id: VUEX,
          type: 'action',
          label: 'org.vue.cli-service.suggestions.vuex-add.label',
          message: 'org.vue.cli-service.suggestions.vuex-add.message',
          link: 'https://vuex.vuejs.org/',
          async handler () {
            await install(api, 'vuex')
          }
        })
      }
    } else {
      [ROUTER, VUEX].forEach(id => api.removeSuggestion(id))
    }

    if (view.id !== 'vue-project-configurations') {
      api.removeSuggestion(VUE_CONFIG_OPEN)
    }
  })

  api.onConfigRead(({ config }) => {
    if (config.id === 'org.vue.vue-cli') {
      if (config.foundFiles.vue) {
        api.addSuggestion({
          id: VUE_CONFIG_OPEN,
          type: 'action',
          label: 'org.vue.vue-webpack.suggestions.vue-config-open',
          handler () {
            const file = config.foundFiles.vue.path
            console.log('open', file)
            const { launch } = require('@vue/cli-shared-utils')
            launch(file)
            return {
              keep: true
            }
          }
        })
        return
      }
    }
    api.removeSuggestion(VUE_CONFIG_OPEN)
  })
}

async function install (api, id) {
  api.setProgress({
    status: 'org.vue.cli-service.suggestions.progress',
    args: [id],
    progress: -1
  })

  const context = api.getCwd()

  let error

  try {
    const servicePkg = loadModule('@vue/cli-service/package.json', context)
    // @vue/cli-plugin-router is not compatible with @vue/cli-service v3,
    // so we have to check for the version and call the right generator
    if (semver.satisfies(servicePkg.version, '3.x')) {
      await invoke.runGenerator(context, {
        id: `core:${id}`,
        apply: loadModule(`@vue/cli-service/generator/${id}`, context)
      })
    } else {
      // FIXME: a temporary fix for adding router plugin
      // should implement a plugin prompt ui later
      await add(id, { $inlineOptions: '{}' }, context)
    }
  } catch (e) {
    error = e
  }

  api.removeProgress()

  if (error) throw error
}


module.exports = (api, options) => {
  api.render('./template', options)
}


keep keep keep keep keep replace keep keep keep


# webpack 相关

## 简单的配置方式

调整 webpack 配置最简单的方式就是在 `vue.config.js` 中的 `configureWebpack` 选项提供一个对象：

``` js
// vue.config.js
module.exports = {
  configureWebpack: {
    plugins: [
      new MyAwesomeWebpackPlugin()
    ]
  }
}
```

该对象将会被 [webpack-merge](https://github.com/survivejs/webpack-merge) 合并入最终的 webpack 配置。

::: warning 警告
有些 webpack 选项是基于 `vue.config.js` 中的值设置的，所以不能直接修改。例如你应该修改 `vue.config.js` 中的 `outputDir` 选项而不是修改 `output.path`；你应该修改 `vue.config.js` 中的 `publicPath` 选项而不是修改 `output.publicPath`。这样做是因为 `vue.config.js` 中的值会被用在配置里的多个地方，以确保所有的部分都能正常工作在一起。
:::

如果你需要基于环境有条件地配置行为，或者想要直接修改配置，那就换成一个函数 (该函数会在环境变量被设置之后懒执行)。该方法的第一个参数会收到已经解析好的配置。在函数内，你可以直接修改配置，或者返回一个将会被合并的对象：

``` js
// vue.config.js
module.exports = {
  configureWebpack: config => {
    if (process.env.NODE_ENV === 'production') {
      // 为生产环境修改配置...
    } else {
      // 为开发环境修改配置...
    }
  }
}
```

## 链式操作 (高级)

Vue CLI 内部的 webpack 配置是通过 [webpack-chain](https://github.com/mozilla-neutrino/webpack-chain) 维护的。这个库提供了一个 webpack 原始配置的上层抽象，使其可以定义具名的 loader 规则和具名插件，并有机会在后期进入这些规则并对它们的选项进行修改。

它允许我们更细粒度的控制其内部配置。接下来有一些常见的在 `vue.config.js` 中的 `chainWebpack` 修改的例子。

::: tip 提示
当你打算链式访问特定的 loader 时，[vue inspect](#审查项目的-webpack-配置) 会非常有帮助。
:::

### 修改 Loader 选项

``` js
// vue.config.js
module.exports = {
  chainWebpack: config => {
    config.module
      .rule('vue')
      .use('vue-loader')
        .loader('vue-loader')
        .tap(options => {
          // 修改它的选项...
          return options
        })
  }
}
```

::: tip 提示
对于 CSS 相关 loader 来说，我们推荐使用 [css.loaderOptions](../config/#css-loaderoptions) 而不是直接链式指定 loader。这是因为每种 CSS 文件类型都有多个规则，而 `css.loaderOptions` 可以确保你通过一个地方影响所有的规则。
:::

### 添加一个新的 Loader

``` js
// vue.config.js
module.exports = {
  chainWebpack: config => {
    // GraphQL Loader
    config.module
      .rule('graphql')
      .test(/\.graphql$/)
      .use('graphql-tag/loader')
        .loader('graphql-tag/loader')
        .end()
      // 你还可以再添加一个 loader
      .use('other-loader')
        .loader('other-loader')
        .end()
  }
}
```

### 替换一个规则里的 Loader

如果你想要替换一个已有的[基础 loader](https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-service/lib/config/base.js)，例如为内联的 SVG 文件使用 `vue-svg-loader` 而不是加载这个文件：

``` js
// vue.config.js
module.exports = {
  chainWebpack: config => {
    const svgRule = config.module.rule('svg')

    // 清除已有的所有 loader。
    // 如果你不这样做，接下来的 loader 会附加在该规则现有的 loader 之后。
    svgRule.uses.clear()

    // 添加要替换的 loader
    svgRule
      .use('vue-svg-loader')
        .loader('vue-svg-loader')
  }
}
```

### 修改插件选项

``` js
// vue.config.js
module.exports = {
  chainWebpack: config => {
    config
      .plugin('html')
      .tap(args => {
        return [/* 传递给 html-webpack-plugin's 构造函数的新参数 */]
      })
  }
}
```

你需要熟悉 [webpack-chain 的 API](https://github.com/mozilla-neutrino/webpack-chain#getting-started) 并[阅读一些源码](https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-service/lib/config)以便了解如何最大程度利用好这个选项，但是比起直接修改 webpack 配置，它的表达能力更强，也更为安全。

比方说你想要将 `index.html` 默认的路径从 */Users/username/proj/public/index.html* 改为 */Users/username/proj/app/templates/index.html*。通过参考 [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin#options) 你能看到一个可以传入的选项列表。我们可以在下列配置中传入一个新的模板路径来改变它：

``` js
// vue.config.js
module.exports = {
  chainWebpack: config => {
    config
      .plugin('html')
      .tap(args => {
        args[0].template = '/Users/username/proj/app/templates/index.html'
        return args
      })
  }
}
```

你可以通过接下来要讨论的工具 **`vue inspect`** 来确认变更。

## 审查项目的 webpack 配置

因为 `@vue/cli-service` 对 webpack 配置进行了抽象，所以理解配置中包含的东西会比较困难，尤其是当你打算自行对其调整的时候。

`vue-cli-service` 暴露了 `inspect` 命令用于审查解析好的 webpack 配置。那个全局的 `vue` 可执行程序同样提供了 `inspect` 命令，这个命令只是简单的把 `vue-cli-service inspect` 代理到了你的项目中。

该命令会将解析出来的 webpack 配置、包括链式访问规则和插件的提示打印到 stdout。

你可以将其输出重定向到一个文件以便进行查阅：

``` bash
vue inspect > output.js
```

注意它输出的并不是一个有效的 webpack 配置文件，而是一个用于审查的被序列化的格式。

你也可以通过指定一个路径来审查配置的一小部分：

``` bash
# 只审查第一条规则
vue inspect module.rules.0
```

或者指向一个规则或插件的名字：

``` bash
vue inspect --rule vue
vue inspect --plugin html
```

最后，你可以列出所有规则和插件的名字：

``` bash
vue inspect --rules
vue inspect --plugins
```

## 以一个文件的方式使用解析好的配置

有些外部工具可能需要通过一个文件访问解析好的 webpack 配置，比如那些需要提供 webpack 配置路径的 IDE 或 CLI。在这种情况下你可以使用如下路径：

```
<projectRoot>/node_modules/@vue/cli-service/webpack.config.js
```

该文件会动态解析并输出 `vue-cli-service` 命令中使用的相同的 webpack 配置，包括那些来自插件甚至是你自定义的配置。


const { log, error, openBrowser } = require('@vue/cli-shared-utils')
const { portfinder, server } = require('@vue/cli-ui/server')
const shortid = require('shortid')

function simpleCorsValidation (allowedHost) {
  return function (req, socket) {
    const { host, origin } = req.headers
    // maybe we should just use strict string equal?
    const hostRegExp = new RegExp(`^https?://(${host}|${allowedHost}|localhost)(:\\d+)?$`)

    if (!origin || !hostRegExp.test(origin)) {
      socket.destroy()
    }
  }
}

async function ui (options = {}, context = process.cwd()) {
  const host = options.host || 'localhost'

  let port = options.port
  if (!port) {
    port = await portfinder.getPortPromise()
  }

  // Config
  process.env.VUE_APP_CLI_UI_URL = ''

  // Optimize express
  const nodeEnv = process.env.NODE_ENV
  process.env.NODE_ENV = 'production'

  // Dev mode
  if (options.dev) {
    process.env.VUE_APP_CLI_UI_DEBUG = true
  }

  if (!process.env.VUE_CLI_IPC) {
    // Prevent IPC id conflicts
    process.env.VUE_CLI_IPC = `vue-cli-${shortid()}`
  }

  if (!options.quiet) log(`🚀  Starting GUI...`)

  const opts = {
    host,
    port,
    graphqlPath: '/graphql',
    subscriptionsPath: '/graphql',
    enableMocks: false,
    enableEngine: false,
    cors: {
      origin: host
    },
    timeout: 1000000,
    quiet: true,
    paths: {
      typeDefs: require.resolve('@vue/cli-ui/apollo-server/type-defs.js'),
      resolvers: require.resolve('@vue/cli-ui/apollo-server/resolvers.js'),
      context: require.resolve('@vue/cli-ui/apollo-server/context.js'),
      pubsub: require.resolve('@vue/cli-ui/apollo-server/pubsub.js'),
      server: require.resolve('@vue/cli-ui/apollo-server/server.js'),
      directives: require.resolve('@vue/cli-ui/apollo-server/directives.js')
    }
  }

  const { httpServer } = server(opts, () => {
    // Reset for yarn/npm to work correctly
    if (typeof nodeEnv === 'undefined') {
      delete process.env.NODE_ENV
    } else {
      process.env.NODE_ENV = nodeEnv
    }

    // Open browser
    const url = `http://${host}:${port}`
    if (!options.quiet) log(`🌠  Ready on ${url}`)
    if (options.headless) {
      console.log(port)
    } else {
      openBrowser(url)
    }
  })

  httpServer.on('upgrade', simpleCorsValidation(host))
}

module.exports = (...args) => {
  return ui(...args).catch(err => {
    error(err)
    if (!process.env.VUE_CLI_TEST) {
      process.exit(1)
    }
  })
}


<template>

{{ $t('org.vue.vue-webpack.test-view') }}

A vue-cli plugin created me! I am a dynamically loaded component paired with a custom route.

      <VueInput
        v-model="testData"
        placeholder="Shared data (with storage)"
      />

      <VueButton @click="testPluginAction()">Test plugin action</VueButton>

</template>


---
sidebar: auto
---

# Migrating from v3

First, install the latest Vue CLI globally:

```sh
npm install -g @vue/cli
# OR
yarn global add @vue/cli
```

## Upgrade All Plugins at Once

In your existing projects, run:

```sh
vue upgrade
```

And then see the following section for detailed breaking changes introduced in each package.

------

## One-By-One Manual Migration

If you want to migrate manually and gradually, here are the tips:

### The Global `@vue/cli`

#### [Redesigned](https://github.com/vuejs/vue-cli/pull/4090) `vue upgrade`

- Before: `vue upgrade [patch | minor | major]`, and it does nothing more than installing the latest versions of Vue CLI plugins.
- After: `vue upgrade [plugin-name]`. Aside from upgrading the plugins, it can run migrators from plugins to help you automate the migration process. For more options for this command, please run `vue upgrade --help`.

#### `vue --version` Output Format Change

When running `vue --version`:

- 3.x: outputs `3.12.0`
- 4.x: outputs `@vue/cli 4.0.0`

#### Extra Confirmation Step To Avoid Overwriting

When running `vue invoke` / `vue add` / `vue upgrade`, there's now an [extra confirmation step](https://github.com/vuejs/vue-cli/pull/4275) if you have uncommitted changes in the current repository.

![image](https://user-images.githubusercontent.com/3277634/65588457-23db5a80-dfba-11e9-9899-9dd72efc111e.png)

#### Vue Router and Vuex Now Have Corresponding CLI Plugins

When running `vue add vuex` or `vue add router`:

- In v3, only `vuex` or `vue-router` will be added to the project;
- In v4, there will also be `@vue/cli-plugin-vuex` or `@vue/cli-plugin-router` installed.

This currently does not make an actual difference for end-users, but such design allows us to add more features for Vuex and Vue Router users later.

For preset and plugin authors, there are several noteworthy changes in the two plugins:

- The default directory structure was changed:
  - `src/store.js` moved to `src/store/index.js`;
  - `src/router.js` renamed to `src/router/index.js`;
- The `router` & `routerHistoryMode` options in `preset.json` are still supported for compatibility reasons. But it's now recommended to use `plugins: { '@vue/cli-plugin-router': { historyMode: true } }` for better consistency.
- `api.hasPlugin('vue-router')` is no longer supported. It's now `api.hasPlugin('router')`.

### `@vue/cli-service`

#### Whitespace handling in the template block

To get a smaller bundle, we've disabled the `preserveWhitespace` option of `vue-template-compiler` by default in Vue CLI v3.

This set comes with some caveats, however.

Luckily, since the Vue 2.6 release, we can now have finer control over the whitespace handling, with the [new `whitespace` option](https://github.com/vuejs/vue/issues/9208#issuecomment-450012518). So we decided to switch over to use this new option by default in Vue CLI v4.

Take the following template as an example:

```html
<p>
  Welcome to <b>Vue.js</b> <i>world</i>.
  Have fun!
</p>
```

With `preserveWhitespace: false`, all whitespaces between tags are removed, so it was compiled as:

```html
<p> Welcome to <b>Vue.js</b><i>world</i>. Have fun! </p>
```

With `whitespace: 'condense'`, it is now compiled as:

```html
<p> Welcome to <b>Vue.js</b> <i>world</i>. Have fun! </p>
```

Note the **inline** whitespace between tags is now preserved.

#### `vue-cli-service build --mode development`

In the past, when running the `build` command in the `development` mode, the `dist` folder layout would be different from the `production` mode. Now with the following two changes, the directory structures across all modes would be the same (file names are still different - no hashes in `development` mode):

- [#4323](https://github.com/vuejs/vue-cli/pull/4323) ensure consistent directory structure for all modes
- [#4302](https://github.com/vuejs/vue-cli/pull/4302) move dev configs into serve command

#### For SASS/SCSS Users

Previously in Vue CLI v3, we shipped with `sass-loader@7` by default.

Recently `sass-loader@8` has been out and has changed its configuration format quite a lot. Here's the release notes: <https://github.com/webpack-contrib/sass-loader/releases/tag/v8.0.0>

`@vue/cli-service` will continue to support `sass-loader@7` in v4, but we strongly recommend you to take a look at the v8 release and upgrade to the latest version.

#### For Less Users

`less-loader` v4 is incompatible with `less` >= v3.10, see <https://github.com/less/less.js/issues/3414>.
It's strongly recommended to upgrade to `less-loader@5` if your project depends on it.

#### For CSS Module Users

- [Deprecate `css.modules` in favor of `css.requireModuleExtension`](https://github.com/vuejs/vue-cli/pull/4387). This is because we've upgraded to `css-loader` v3 and the config format has been changed. For a more detailed explanation please follow the link.

#### `vue.config.js` options

The already-deprecated [`baseUrl` option](https://cli.vuejs.org/config/#baseurl) is now [removed](https://github.com/vuejs/vue-cli/pull/4388)

#### `chainWebpack` / `configureWebpack`

##### The `minimizer` Method in `chainWebpack`

If you've customized the internal rules with `chainWebpack`, please notice that `webpack-chain` was updated from v4 to v6, the most noticeable change is the `minimizer` config

For example, if you want to enable the `drop_console` option in the terser plugin.
In v3, you may do this in `chainWebpack`:

```js
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
  chainWebpack: (config) => {
    config.optimization.minimizer([
      new TerserPlugin({ terserOptions: { compress: { drop_console: true } } })
    ])
  }
}
```

In v4, it's changed to:

```js
module.exports = {
  chainWebpack: (config) => {
    config.optimization.minimizer('terser').tap((args) => {
      args[0].terserOptions.compress.drop_console = true
      return args
    })
  }
}
```

##### Other Changes

- [The `pug-plain` rule was renamed to `pug-plain-loader`](https://github.com/vuejs/vue-cli/pull/4230)

#### Underlying Loaders / Plugins

Not likely to affect users unless you've customized their options via `chainWebpack` / `configureWebpack`

`css-loader` was upgraded from v1 to v3:

- [v2 changelog](https://github.com/webpack-contrib/css-loader/releases/tag/v2.0.0)
- [v3 changelog](https://github.com/webpack-contrib/css-loader/releases/tag/v3.0.0)

Several other underlying webpack loaders and plugins have been upgraded, with mostly trivial changes:

- `url-loader` [from v1 to v2](https://github.com/webpack-contrib/url-loader/releases/tag/v2.0.0)
- `file-loader` [from v3 to v4](https://github.com/webpack-contrib/file-loader/releases/tag/v4.0.0)
- `copy-webpack-plugin` [from v4 to v5](https://github.com/webpack-contrib/copy-webpack-plugin/blob/master/CHANGELOG.md#500-2019-02-20)
- `terser-webpack-plugin` [from v1 to v2](https://github.com/vuejs/vue-cli/pull/4676)

### `@vue/cli-plugin-babel`, `@vue/babel-preset-app`

#### core-js

The babel plugin requires a peer dependency, for the polyfills used in the transpiled code.

In Vue CLI v3, the required `core-js` version is 2.x, it is now upgraded to 3.x.

This migration is automated if you upgrade it through `vue upgrade babel`. But if you have custom polyfills introduced, you may need to manually update the polyfill names (For more details, see [core-js changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md#L279-L297)).

#### Babel Preset

This migration is also automated if you upgrade it through `vue upgrade babel`.

- In v3, the default babel preset used in `babel.config.js` is `@vue/app`.
- In v4, we moved it to the plugin, so now it's named as `@vue/cli-plugin-babel/preset`

It is because that `@vue/babel-preset-app` is indeed an indirect dependency on the project.
It works because of npm's package hoisting.
But potential problems could still occur if the project has multiple conflicting indirect dependencies of the same package, or if the package manager puts stricter constraints on the dependency resolution (e.g. yarn plug'n'play or pnpm).
So we now moved it to the project's direct dependency (`@vue/cli-plugin-babel`) to make it more standard-compliant and less error-prone.

------

### `@vue/cli-plugin-eslint`

This plugin now [requires ESLint as a peer dependency](https://github.com/vuejs/vue-cli/pull/3852).

This won't affect projects scaffolded with Vue CLI 3.1 or later.

If your project was scaffolded with Vue CLI 3.0.x or earlier, you may need to add `eslint@4` to your project dependencies (This is automated if you upgrade the plugin using `vue upgrade eslint`).

It's also recommended to upgrade your ESLint to v5, and ESLint config versions to the latest. (ESLint v6 support is still on the way)

------

#### The Prettier Preset

The old implementation of our prettier preset is flawed. We've updated the default template since Vue CLI v3.10.

It now requires `eslint`, `eslint-plugin-prettier` and `prettier` as peer dependencies, following the [standard practice in the ESLint ecosystem](https://github.com/eslint/eslint/issues/3458).

For older projects, if you encountered issues like `Cannot find module: eslint-plugin-prettier`, please run the following command to fix it:

```sh
npm install --save-dev eslint@5 @vue/eslint-config-prettier@5 eslint-plugin-prettier prettier
```

------

#### `lintOnSave` options

(the following only affects development)

The default value of `lintOnSave` option (when not specified) was [changed from `true` to `'default'`](https://github.com/vuejs/vue-cli/pull/3975). You can read more on the detailed explanation in the [documentation](https://cli.vuejs.org/config/#lintonsave).

In a nutshell:

- In v3, by default, lint warnings, along with errors, will be displayed in the error overlay
- In v4, by default, only lint errors will interrupt your development process. Warnings are only logged in the terminal console.

### `@vue/cli-plugin-pwa`

The underlying workbox-webpack-plugin has been upgraded from v3 to v4. See [the release notes here](https://github.com/GoogleChrome/workbox/releases/tag/v4.0.0).

There's also a `pwa.manifestOptions` field available (you can set it in the `vue.config.js`). With this new option, `manifest.json` will be generated from the config object rather than directly copied from the `public` folder. This provides a more consistent interface to manage your PWA configurations. (Note, it is an opt-in feature. Related PRs: [#2981](https://github.com/vuejs/vue-cli/pull/2981), [#4664](https://github.com/vuejs/vue-cli/pull/4664))

### `@vue/cli-plugin-e2e-cypress`

Before Vue CLI v3.0.0-beta.10, the default command for E2E testing was `vue-cli-service e2e`. Later we changed it to `vue-cli-service test:e2e`. The previous command was since deprecated but still supported.
We have now completely [dropped support for this legacy command](https://github.com/vuejs/vue-cli/pull/3774).

### `@vue/cli-plugin-e2e-nightwatch`

Nightwatch.js has been upgraded from 0.9 to 1.x. Be sure to read the [Nightwatch migration guides](https://github.com/nightwatchjs/nightwatch/wiki/Migrating-to-Nightwatch-1.0) first.

The bundled config and generated tests [have been completely overhauled](https://github.com/vuejs/vue-cli/pull/4541). Please follow the link for more details. Most use cases in Vue CLI v3 are still supported. They are just new features.

As ChromeDriver has changed its version strategy since version 73, we've made it a peer dependency in the project.
A simple browser version check is implemented in the plugin, so if you've upgraded to an incompatible version of Chrome, there will be a warning to prompt you to upgrade the depended ChromeDriver version.

------

As in the cypress plugin, the support for legacy `vue-cli-service e2e` command has also been removed.

### `@vue/cli-plugin-typescript`

When importing a file without extension, the webpack resolve options now [prefer `.ts(x)` files over `.js(x)` and `.vue` ones](https://github.com/vuejs/vue-cli/pull/3909). We strongly recommend you to always include the file extension when importing `.vue` files.

### `@vue/cli-plugin-unit-jest`

We've upgraded the bundled Jest from v23 to v24, so please read their [release notes](https://jestjs.io/blog/2019/01/25/jest-24-refreshing-polished-typescript-friendly) first.
Follow [this link](https://github.com/facebook/jest/blob/20ba4be9499d50ed0c9231b86d4a64ec8a6bd303/CHANGELOG.md#user-content-2400) for the full changelog.

The `unit-jest` plugin now comes with 4 configuration presets:

- `@vue/cli-plugin-unit-jest` The default preset for the most common type of projects
- `@vue/cli-plugin-unit-jest/presets/no-babel` If you don't have `@vue/cli-plugin-babel` installed and don't want to see babel files in the project
- `@vue/cli-plugin-unit-jest/presets/typescript` The preset with TypeScript support (but no TSX support)
- `@vue/cli-plugin-unit-jest/presets/typescript-and-babel` The preset with TypeScript (and TSX) and babel support.

If you haven't changed the default Jest configurations (lies in either `jest.config.js` or the `jest` field in `package.json`) ever since project creation, you can now replace the massive configuration object with one single field:

```js
module.exports = {
  // Replace the following preset name with the one you want to use from the above list
  preset: '@vue/cli-plugin-unit-jest'
}
```

(the `ts-jest`, `babel-jest` dependencies can also be removed after migrating config to use presets)

::: tip A Reminder
The default test environment in the new presets is jsdom@15, which differs from the default one in Jest 24 (jsdom@11).
This is to be aligned with the upcoming Jest 25 updates.
Most users won't be affected by this change.
For a detailed changelog with regard to jsdom, see <https://github.com/jsdom/jsdom/blob/master/Changelog.md>
:::

### `@vue/cli-plugin-unit-mocha`

- Use mochapack instead of mocha-webpack, see changelog at <https://github.com/sysgears/mochapack/releases>. This change is not likely to affect actual usage.
- Upgraded to mocha 6, see [Mocha's changelog](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md#600-0--2019-01-01) for more details.

### `@vue/cli-service-global`

See breaking changes in the [`@vue/cli-service`](#vue-cli-service) & [`@vue/cli-plugin-eslint`](#vue-cli-plugin-eslint) packages.


# Совместимость с браузерами

## browserslist

Вы заметите поле `browserslist` в файле `package.json` (или файл `.browserslistrc`), где определяется диапазон браузеров под которые разрабатывается проект. Эти значения будут использоваться в [@babel/preset-env][babel-preset-env] и [autoprefixer][autoprefixer] для автоматического определения возможностей JavaScript, которые требуется транспилировать, а также необходимые префиксные правила CSS.

Как указывается диапазон браузеров, можно узнать [здесь][browserslist].

## Полифилы

По умолчанию проект Vue CLI использует [@vue/babel-preset-app][babel-preset-app], в котором используется `@babel/preset-env` и конфигурация `browserslist` для определения необходимых полифилов.

### useBuiltIns: 'usage'

По умолчанию в `@babel/preset-env` будет передаваться [`useBuiltIns: 'usage'`](https://new.babeljs.io/docs/en/next/babel-preset-env.html#usebuiltins-usage) для автоматического определения необходимых полифилов, основываясь на том, какие возможности языка были использованы в исходном коде проекта. Это гарантирует, что в финальную сборку попадёт только минимально необходимое количество полифилов. Однако это также означает, что **если одна из ваших зависимостей имеет специфичные требования к полифилам, то по умолчанию Babel не сможет это определить.**

Если одной из ваших зависимостей требуются полифилы, у вас есть несколько вариантов:

1. **Если зависимость написана в версии ES, которую не поддерживают целевые окружения:** Добавьте эту зависимость в опцию [`transpileDependencies`](../config/#transpiledependencies) в файле `vue.config.js`. Это позволит использовать как синтаксические преобразования, так и определение полифилов для используемых возможностей для этой зависимости.

2. **Если зависимость предоставляет ES5 код и явно перечисляет необходимые полифилы:** вы можете предварительно включить необходимые полифилы с помощью опции [polyfills](https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/babel-preset-app#polyfills) для `@vue/babel-preset-app`. **Обратите внимание, что `es6.promise` добавлен по умолчанию, так как он  часто необходим для библиотек, основанных на Promise.**

    ```js
    // babel.config.js
    module.exports = {
      presets: [
        ['@vue/app', {
          polyfills: [
            'es6.promise',
            'es6.symbol'
          ]
        }]
      ]
    }
    ```

    ::: tip Совет
    Рекомендуется добавлять полифилы таким образом, а не напрямую импортировать их в коде, потому что полифилы перечисленные здесь, могут быть автоматически исключены, если целевым браузерам, указанным в `browserslist`, они не нужны.
    :::

3. **Если зависимость предоставляет ES5 код, но использует возможности ES6+ без явного перечисления необходимых полифилов (например, Vuetify):** Используйте `useBuiltIns: 'entry'` и затем добавьте `import '@babel/polyfill'` в файл точки входа. Это будет импортировать **ВСЕ** полифилы на основе целей, перечисленных в `browserslist`, так что вам больше не нужно будет беспокоиться о полифилах для зависимостей, но это скорее всего увеличит размер финальной сборки некоторыми неиспользуемыми полифилами.

Подробнее можно изучить в [документации @babel-preset/env](https://new.babeljs.io/docs/en/next/babel-preset-env.html#usebuiltins-usage).

### Полифилы при сборке библиотеки или веб-компонентов

При использовании Vue CLI для [сборки библиотеки или веб-компонентов](./build-targets.md) рекомендуется указывать `useBuiltIns: false` для `@vue/babel-preset-app`, чтобы отключить автоматическое добавление полифилов. Это гарантирует, что вы не добавляете ненужные полифилы в свой код, потому что полифилами должно будет заниматься приложение, где они будут использоваться.

## Современный режим

Благодаря Babel мы можем использовать все новейшие возможности языка ES2015+, но это также означает, что нам необходимо предоставлять транспилированную сборку с полифилами для поддержки старых браузеров. Эти транспилированные сборки зачастую больше в размере, чем оригинальный исходный код в ES2015+, а их парсинг и выполнение происходит медленнее. Учитывая, что сегодня у большинства современных браузеров есть прекрасная поддержка ES2015, становится пустой тратой необходимость предоставлять более тяжёлый и менее эффективный код для них лишь потому, что мы должны поддерживать старые версии браузеров.

Vue CLI предоставляет «Современный режим», чтобы помочь в решении этой проблемы. При сборке для production с помощью следующей команды:

```bash
vue-cli-service build --modern
```

Vue CLI будет собирать **две версии** вашего приложения: первая сборка для современных браузеров, которые поддерживают [ES-модули](https://jakearchibald.com/2017/es-modules-in-browsers/), а вторая сборка для старых браузеров, которые не имеют такой поддержки.

Приятная часть заключается в том, что ничего специально делать при публикации не требуется. Сгенерированный HTML-файл автоматически использует технику, описанную в классном [посте Филлипа Уолтона](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/):

- Современная сборка загружается с помощью `<script type="module">` в браузерах, которые поддерживают модули; они также предзагружаются через `<link rel="modulepreload">`.

- Сборка для старых браузеров загружается с помощью тега `<script nomodule>`, который игнорируется браузерами, поддерживающими ES-модули.

- Исправление ошибки для `<script nomodule>` в Safari 10 также внедряется автоматически.

Для приложения Hello World современная сборка на 16% меньше. В production использование современной сборки приводит к значительному ускорению парсинга и исполнения кода, что повышает производительность загрузки приложения.

::: tip Совет
`<script type="module">` загружаются [всегда с помощью CORS](https://jakearchibald.com/2017/es-modules-in-browsers/#always-cors). Это значит, что ваш сервер должен возвращать корректные заголовки CORS, такие как `Access-Control-Allow-Origin: *`. Если вы хотите загружать скрипты с помощью credentials, установите опцию [crossorigin](../config/#crossorigin) в значение `use-credentials`.

Кроме того, современный режим использует инлайновый скрипт для избежания загрузки обеих сборок в Safari 10, поэтому, если вы используете строгие политики CSP, необходимо явно разрешить инлайновый скрипт с помощью:

```
Content-Security-Policy: script-src 'self' 'sha256-4RS22DYeB7U14dra4KcQYxmwt5HkOInieXK1NUMBmQI='
```
:::

::: tip Определение текущего режима в конфигурации
Иногда может потребоваться изменить конфигурацию webpack только сборки для старых браузеров или только сборки для современных браузеров.

Vue CLI использует две переменных окружения для определения этого:

* `VUE_CLI_MODERN_MODE`: флаг, что сборка была запущена с флагом `--modern`
* `VUE_CLI_MODERN_BUILD`: значение true будет, если текущая конфигурация для современной сборки. В противном случае это сборка для старых браузеров.

**Важно:** Переменные доступны только при вызове/после вызова функций `chainWebpack()` и `configureWebpack()`, (т.е. не напрямую в области видимости модуля `vue.config.js`). Это также означает, что они доступны в файле конфигурации postcss.
:::

::: warning Предостережение: Настройка плагинов webpack
Некоторые плагины, такие как `html-webpack-plugin`, `preload-plugin` и т.п., добавляются только в конфигурации для современного режима. Попытка использовать их опции в конфигурации для старых браузеров может привести к ошибке, так как этих плагинов не будет существовать.

Используйте совет выше об *Определении текущего режима в конфигурации* для управления плагинами только в соответствующем режиме, и/или проверяйте наличие плагина в конфигурации текущего режима, прежде чем использовать их опции.
:::

[autoprefixer]: https://github.com/postcss/autoprefixer
[babel-preset-env]: https://new.babeljs.io/docs/en/next/babel-preset-env.html
[babel-preset-app]: https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/babel-preset-app
[browserslist]: https://github.com/ai/browserslist


const path = require('path')
const fs = require('fs')
const shortId = require('shortid')
const Creator = require('@vue/cli/lib/Creator')
const { getPromptModules } = require('@vue/cli/lib/util/createTools')
const { getFeatures } = require('@vue/cli/lib/util/features')
const { defaults } = require('@vue/cli/lib/options')
const { toShortPluginId, execa } = require('@vue/cli-shared-utils')
const { progress: installProgress } = require('@vue/cli/lib/util/executeCommand')
const parseGitConfig = require('parse-git-config')
// Connectors
const progress = require('./progress')
const cwd = require('./cwd')
const prompts = require('./prompts')
const folders = require('./folders')
const plugins = require('./plugins')
const locales = require('./locales')
const logs = require('./logs')
// Context
const getContext = require('../context')
// Utils
const { log } = require('../util/logger')
const { notify } = require('../util/notification')
const { getHttpsGitURL } = require('../util/strings')

const PROGRESS_ID = 'project-create'

let lastProject = null
let currentProject = null
let creator = null
let presets = []
let features = []
let onCreationEvent = null
let onInstallProgress = null
let onInstallLog = null

function list (context) {
  let projects = context.db.get('projects').value()
  projects = autoClean(projects, context)
  return projects
}

function findOne (id, context) {
  return context.db.get('projects').find({ id }).value()
}

function findByPath (file, context) {
  return context.db.get('projects').find({ path: file }).value()
}

function autoClean (projects, context) {
  const result = []
  for (const project of projects) {
    if (fs.existsSync(project.path)) {
      result.push(project)
    }
  }
  if (result.length !== projects.length) {
    console.log(`Auto cleaned ${projects.length - result.length} projects (folder not found).`)
    context.db.set('projects', result).write()
  }
  return result
}

function getCurrent (context) {
  if (currentProject && !fs.existsSync(currentProject.path)) {
    log('Project folder not found', currentProject.id, currentProject.path)
    return null
  }
  return currentProject
}

function getLast (context) {
  return lastProject
}

function generatePresetDescription (preset) {
  let description = preset.features.join(', ')
  if (preset.raw.useConfigFiles) {
    description += ' (Use config files)'
  }
  return description
}

function generateProjectCreation (creator) {
  return {
    presets,
    features,
    prompts: prompts.list()
  }
}

async function initCreator (context) {
  const creator = new Creator('', cwd.get(), getPromptModules())

  /* Event listeners */
  // Creator emits creation events (the project creation steps)
  onCreationEvent = ({ event }) => {
    progress.set({ id: PROGRESS_ID, status: event, info: null }, context)
  }
  creator.on('creation', onCreationEvent)
  // Progress bar
  onInstallProgress = value => {
    if (progress.get(PROGRESS_ID)) {
      progress.set({ id: PROGRESS_ID, progress: value }, context)
    }
  }
  installProgress.on('progress', onInstallProgress)
  // Package manager steps
  onInstallLog = message => {
    if (progress.get(PROGRESS_ID)) {
      progress.set({ id: PROGRESS_ID, info: message }, context)
    }
  }
  installProgress.on('log', onInstallLog)

  // Presets
  const manualPreset = {
    id: '__manual__',
    name: 'org.vue.views.project-create.tabs.presets.manual.name',
    description: 'org.vue.views.project-create.tabs.presets.manual.description',
    link: null,
    features: []
  }
  const presetsData = creator.getPresets()
  presets = [
    ...Object.keys(presetsData).map(
      key => {
        const preset = presetsData[key]
        const features = getFeatures(preset).map(
          f => toShortPluginId(f)
        )
        const info = {
          id: key,
          name: key === 'default' ? 'org.vue.views.project-create.tabs.presets.default-preset' : key,
          features,
          link: null,
          raw: preset
        }
        info.description = generatePresetDescription(info)
        return info
      }
    ),
    manualPreset
  ]

  // Features
  const featuresData = creator.featurePrompt.choices
  features = [
    ...featuresData.map(
      data => ({
        id: data.value,
        name: data.name,
        description: data.description || null,
        link: data.link || null,
        plugins: data.plugins || null,
        enabled: !!data.checked
      })
    ),
    {
      id: 'use-config-files',
      name: 'org.vue.views.project-create.tabs.features.userConfigFiles.name',
      description: 'org.vue.views.project-create.tabs.features.userConfigFiles.description',
      link: null,
      plugins: null,
      enabled: false
    }
  ]

  manualPreset.features = features.filter(
    f => f.enabled
  ).map(
    f => f.id
  )

  // Prompts
  await prompts.reset()
  creator.injectedPrompts.forEach(prompts.add)
  await updatePromptsFeatures()
  await prompts.start()

  return creator
}

function removeCreator (context) {
  if (creator) {
    creator.removeListener('creation', onCreationEvent)
    installProgress.removeListener('progress', onInstallProgress)
    installProgress.removeListener('log', onInstallLog)
    creator = null
  }
  return true
}

async function getCreation (context) {
  if (!creator) {
    creator = await initCreator(context)
  }
  return generateProjectCreation(creator)
}

async function updatePromptsFeatures () {
  await prompts.changeAnswers(answers => {
    answers.features = features.filter(
      f => f.enabled
    ).map(
      f => f.id
    )
  })
}

async function setFeatureEnabled ({ id, enabled, updatePrompts = true }, context) {
  const feature = features.find(f => f.id === id)
  if (feature) {
    feature.enabled = enabled
  } else {
    console.warn(`Feature '${id}' not found`)
  }
  if (updatePrompts) await updatePromptsFeatures()
  return feature
}

async function applyPreset (id, context) {
  const preset = presets.find(p => p.id === id)
  if (preset) {
    for (const feature of features) {
      feature.enabled = !!(
        preset.features.includes(feature.id) ||
        (feature.plugins && preset.features.some(f => feature.plugins.includes(f)))
      )
    }
    if (preset.raw) {
      if (preset.raw.router) {
        await setFeatureEnabled({ id: 'router', enabled: true, updatePrompts: false }, context)
      }
      if (preset.raw.vuex) {
        await setFeatureEnabled({ id: 'vuex', enabled: true, updatePrompts: false }, context)
      }
      if (preset.raw.cssPreprocessor) {
        await setFeatureEnabled({ id: 'css-preprocessor', enabled: true, updatePrompts: false }, context)
      }
      if (preset.raw.useConfigFiles) {
        await setFeatureEnabled({ id: 'use-config-files', enabled: true, updatePrompts: false }, context)
      }
    }
    await updatePromptsFeatures()
  } else {
    console.warn(`Preset '${id}' not found`)
  }

  return generateProjectCreation(creator)
}

async function create (input, context) {
  return progress.wrap(PROGRESS_ID, context, async setProgress => {
    setProgress({
      status: 'creating'
    })

    const targetDir = path.join(cwd.get(), input.folder)

    cwd.set(targetDir, context)
    creator.context = targetDir

    const inCurrent = input.folder === '.'
    const name = creator.name = (inCurrent ? path.relative('../', process.cwd()) : input.folder).toLowerCase()

    // Answers
    const answers = prompts.getAnswers()
    await prompts.reset()

    // Config files
    let index
    if ((index = answers.features.indexOf('use-config-files')) !== -1) {
      answers.features.splice(index, 1)
      answers.useConfigFiles = 'files'
    }

    // Preset
    answers.preset = input.preset
    if (input.save) {
      answers.save = true
      answers.saveName = input.save
    }

    setProgress({
      info: 'Resolving preset...'
    })
    let preset
    if (input.preset === '__remote__' && input.remote) {
      // vue create foo --preset bar
      preset = await creator.resolvePreset(input.remote, input.clone)
    } else if (input.preset === 'default') {
      // vue create foo --default
      preset = defaults.presets.default
    } else {
      preset = await creator.promptAndResolvePreset(answers)
    }
    setProgress({
      info: null
    })

    // Create
    const args = [
      '--skipGetStarted'
    ]
    if (input.packageManager) args.push('--packageManager', input.packageManager)
    if (input.bar) args.push('--bare')
    if (input.force) args.push('--force')
    // Git
    if (input.enableGit && input.gitCommitMessage) {
      args.push('--git', input.gitCommitMessage)
    } else if (!input.enableGit) {
      args.push('--no-git')
    }
    // Preset
    args.push('--inlinePreset', JSON.stringify(preset))

    log('create', name, args)

    const child = execa('vue', [
      'create',
      name,
      ...args
    ], {
      cwd: cwd.get(),
      stdio: ['inherit', 'pipe', 'inherit']
    })

    const onData = buffer => {
      const text = buffer.toString().trim()
      if (text) {
        setProgress({
          info: text
        })
        logs.add({
          type: 'info',
          message: text
        }, context)
      }
    }

    child.stdout.on('data', onData)

    await child

    removeCreator()

    notify({
      title: 'Project created',
      message: `Project ${cwd.get()} created`,
      icon: 'done'
    })

    return importProject({
      path: targetDir
    }, context)
  })
}

async function importProject (input, context) {
  if (!input.force && !fs.existsSync(path.join(input.path, 'node_modules'))) {
    throw new Error('NO_MODULES')
  }

  const project = {
    id: shortId.generate(),
    path: input.path,
    favorite: 0,
    type: folders.isVueProject(input.path) ? 'vue' : 'unknown'
  }
  const packageData = folders.readPackage(project.path, context)
  project.name = packageData.name
  context.db.get('projects').push(project).write()
  return open(project.id, context)
}

async function open (id, context) {
  const project = findOne(id, context)

  if (!project) {
    log('Project not found', id)
    return null
  }

  if (!fs.existsSync(project.path)) {
    log('Project folder not found', id, project.path)
    return null
  }

  lastProject = currentProject
  currentProject = project
  cwd.set(project.path, context)
  // Reset locales
  locales.reset(context)
  // Load plugins
  await plugins.list(project.path, context)

  // Date
  context.db.get('projects').find({ id }).assign({
    openDate: Date.now()
  }).write()

  // Save for next time
  context.db.set('config.lastOpenProject', id).write()

  log('Project open', id, project.path)

  return project
}

async function remove (id, context) {
  if (currentProject && currentProject.id === id) {
    currentProject = null
  }
  context.db.get('projects').remove({ id }).write()
  if (context.db.get('config.lastOpenProject').value() === id) {
    context.db.set('config.lastOpenProject', undefined).write()
  }
  return true
}

function resetCwd (context) {
  if (currentProject) {
    cwd.set(currentProject.path, context)
  }
}

function setFavorite ({ id, favorite }, context) {
  context.db.get('projects').find({ id }).assign({ favorite }).write()
  return findOne(id, context)
}

function rename ({ id, name }, context) {
  context.db.get('projects').find({ id }).assign({ name }).write()
  return findOne(id, context)
}

function getType (project, context) {
  if (typeof project === 'string') {
    project = findByPath(project, context)
  }
  if (!project) return 'unknown'
  return !project.type ? 'vue' : project.type
}

function getHomepage (project, context) {
  const gitConfigPath = path.join(project.path, '.git', 'config')
  if (fs.existsSync(gitConfigPath)) {
    const gitConfig = parseGitConfig.sync({ path: gitConfigPath })
    const gitRemoteUrl = gitConfig['remote "origin"']
    if (gitRemoteUrl) {
      return getHttpsGitURL(gitRemoteUrl.url)
    }
  }

  const pkg = folders.readPackage(project.path, context)
  return pkg.homepage
}

// Open last project
async function autoOpenLastProject () {
  const context = getContext()
  const id = context.db.get('config.lastOpenProject').value()
  if (id) {
    try {
      await open(id, context)
    } catch (e) {
      log('Project can\'t be auto-opened', id)
    }
  }
}

autoOpenLastProject()

module.exports = {
  list,
  findOne,
  findByPath,
  getCurrent,
  getLast,
  getCreation,
  applyPreset,
  setFeatureEnabled,
  create,
  import: importProject,
  open,
  remove,
  resetCwd,
  setFavorite,
  rename,
  initCreator,
  removeCreator,
  getType,
  getHomepage
}


"xterm": "^3.13.2"
},
"browserslist": [
"> 1%",
"last 2 versions"
],
"main": "index.js",
"license": "MIT",

keep keep keep keep replace keep keep keep

@vue/cli-ui

cross-env VUE_APP_CLI_UI_URL=ws://localhost:4030/graphql VUE_APP_GRAPHQL_PORT=4030 vue-cli-service serve

cross-env VUE_APP_CLI_UI_DEV=true VUE_APP_CLI_UI_DEBUG=true VUE_APP_GRAPHQL_PORT=4030 vue-cli-service apollo:dev

cross-env VUE_CLI_DEBUG=true yarn run apollo

cross-env VUE_CLI_PLUGIN_DEV=true VUE_CLI_IPC=vue-cli-dev vue-cli-service apollo:start

cross-env VUE_CLI_DEBUG=true VUE_CLI_UI_TEST=true VUE_APP_GRAPHQL_PORT=4040 VUE_APP_CLI_UI_URL=ws://localhost:4040/graphql VUE_CLI_IPC=vue-cli-test vue-cli-service apollo:dev --mode production

yarn run lint --no-fix && yarn run build

yarn run test:clear && start-server-and-test apollo:start:test http://localhost:4040/.well-known/apollo/server-health test:e2e:dev

yarn run test:clear && start-server-and-test apollo:start:test http://localhost:4040/.well-known/apollo/server-health test:e2e:start

cross-env VUE_APP_CLI_UI_URL=ws://localhost:4040/graphql vue-cli-service test:e2e --mode development

vue-cli-service test:e2e --mode production --headless --url=http://localhost:4040

rimraf ../../test/cli-ui-test && rimraf ./live-test

yarn run build && cd ../cli-ui-addon-webpack && yarn run build && cd ../cli-ui-addon-widgets && yarn run build && cd ../cli-ui && yarn run test:start

apollo-server dist locales src ui-defaults ui-public index.js server.js graphql-server.js


const fn = require('../lib/convertLintFlags')

test('convert ESLint flags to TSLint flags', () => {
  expect(fn(`
/* eslint-disable */
/* eslint-disable no-console, foo-bar, haha */
// eslint-disable-next-line
// eslint-disable-next-line no-console, foo-bar, haha
foo() // eslint-disable-line
foo() // eslint-disable-line no-console, foo-bar, haha
  `)).toMatch(`
/* tslint:disable */
/* tslint:disable:no-console, foo-bar, haha */
// tslint:disable-next-line
// tslint:disable-next-line:no-console, foo-bar, haha
foo() // tslint:disable-line
foo() // tslint:disable-line:no-console, foo-bar, haha
  `)
})


<template>

    <template v-if="configuration">

 1" class="tabs">
        <VueGroup
          v-model="currentTab"
          class="tabs-selector"
        >
          <VueGroupButton
            v-for="tab of configuration.tabs"
            :key="tab.id"
            :value="tab.id"
            :icon-left="tab.icon"
            :label="$t(tab.label)"
          />
        </VueGroup>

        <ConfigurationTab
          v-for="tab of configuration.tabs"
          v-show="tab.id === currentTab"
          :key="tab.id"
          :configuration="configuration"
          :tab="tab"
          @has-changes="value => tabsHaveChanges[tab.id] = value"
        />

    </template>

    <VueLoadingIndicator
      v-else
      class="loading"
    />

      <VueButton
        v-if="configuration && configuration.link"
        icon-right="open_in_new"
        class="big flat success"
        :label="$t('org.vue.views.project-configuration-details.actions.more-info')"
        :href="configuration.link"
        target="_blank"
      />

      <VueButton
        :disabled="!hasPromptsChanged"
        icon-left="cancel"
        class="big"
        :label="$t('org.vue.views.project-configuration-details.actions.cancel')"
        @click="cancel()"
      />

      <VueButton
        v-if="configuration && !hasPromptsChanged"
        icon-left="refresh"
        class="big primary"
        :label="$t('org.vue.views.project-configuration-details.actions.refresh')"
        @click="refetch()"
      />

      <VueButton
        v-else
        icon-left="save"
        class="primary big"
        :label="$t('org.vue.views.project-configuration-details.actions.save')"
        @click="save()"
      />

</template>


#import "./localeFragment.gql"

query locales {
  locales {
    ...locale
  }
}


keep keep keep replace keep keep keep keep

'.swiper-lazy-preloader {animation: swiper-preloader-spin 1s infinite linear;',

import fs from 'fs';
import path from 'path';
import * as url from 'url';
import autoprefixer from './autoprefixer.js';
import less from './less.js';
import cleanCss from './clean-css.js';
import config from '../build-config.js';

const __dirname = url.fileURLToPath(new URL('.', import.meta.url));

export const getSplittedCSS = (content) => {
  const cssStylesSlideCore = (content.split(`/* Slide styles start */`)[1] || '').split(
    `/* Slide styles end */`,
  )[0];
  const cssStylesSlideCube = (content.split(`/* Cube slide shadows start */`)[1] || '').split(
    `/* Cube slide shadows end */`,
  )[0];
  const cssStylesSlideFlip = (content.split(`/* Flip slide shadows start */`)[1] || '').split(
    `/* Flip slide shadows end */`,
  )[0];
  const cssStylesSlideZoom = (content.split(`/* Zoom container styles start */`)[1] || '').split(
    `/* Zoom container styles end */`,
  )[0];
  const navigationFontStyles = (content.split('/* Navigation font start */')[1] || '').split(
    '/* Navigation font end */',
  )[0];
  content = content
    .replace(cssStylesSlideCore, '')
    .replace(cssStylesSlideCube, '')
    .replace(cssStylesSlideFlip, '')
    .replace(navigationFontStyles, '')
    .replace(cssStylesSlideZoom, '');
  if (content.includes(`/* FONT_END */`)) {
    content = content.split('/* FONT_END */')[1];
  }

  return {
    slides: [
      cssStylesSlideCore || '',
      cssStylesSlideCube || '',
      cssStylesSlideFlip || '',
      cssStylesSlideZoom || '',
    ].join('\n'),
    container: content,
  };
};
export const proceedReplacements = (content = '') => {
  // eslint-disable-next-line
  const replace = ['invisible-blank', 'visible', 'zoomed', 'active', 'next', 'prev'];

  content = content
    .replace(/:root/g, ':host')
    .split('\n')
    .map((line) => {
      if (line.includes('.swiper {')) {
        return line.replace('.swiper {', '.swiper {width: 100%; height: 100%;');
      }
      if (line.includes('> .swiper-wrapper > .swiper-slide')) {
        return line.replace('> .swiper-wrapper > .swiper-slide', '::slotted(swiper-slide)');
      }
      let replaced = '';
      if (line.includes('.swiper-slide ')) {
        replaced = line.replaceAll(/\.swiper-slide /g, '::slotted(swiper-slide) ');
      }
      if (line.includes('.swiper-slide,')) {
        replaced = line.replaceAll(/\.swiper-slide,/g, '::slotted(swiper-slide),');
      }
      if (line.includes('.swiper-slide:')) {
        replaced = line.replaceAll(/\.swiper-slide:/g, '::slotted(swiper-slide):');
      }
      replace.forEach((key) => {
        const l = replaced || line;
        if (l.includes(`.swiper-slide-${key}`)) {
          replaced = l.replaceAll(`.swiper-slide-${key}`, `::slotted(.swiper-slide-${key})`);
        }
      });
      if (replaced) {
        return replaced;
      }

      return line;
    })
    .join('\n');
  return content;
};
const proceedSlideReplacements = (content) => {
  content = content
    .split('\n')
    .map((line) => {
      if (line === '.swiper-lazy-preloader {') {
        return line.replace(
          '.swiper-lazy-preloader {',
          '::slotted(.swiper-lazy-preloader) {animation: swiper-preloader-spin 1s infinite linear;',
        );
      }
      if (line.includes('animation: swiper-preloader-spin 1s infinite linear;')) {
        return '';
      }
      if (line.includes('.swiper-zoom-container')) {
        return line.replace('.swiper-zoom-container', '::slotted(.swiper-zoom-container)');
      }
      if (line.includes('--swiper-preloader-color:')) return '';
      if (line.includes('.swiper-3d .swiper-slide-shadow')) {
        return line.replace('.swiper-3d ', '::slotted(').replace(',', '),').replace(' {', ') {');
      }
      if (line.includes('.swiper-cube .swiper-slide-shadow')) {
        return line.replace('.swiper-cube ', '::slotted(').replace(',', '),').replace(' {', ') {');
      }
      if (line.includes('.swiper-flip .swiper-slide-shadow')) {
        return line.replace('.swiper-flip ', '::slotted(').replace(',', '),').replace(' {', ') {');
      }
      return line;
    })
    .join('\n');
  return content;
};

export default async function getElementStyles() {
  // eslint-disable-next-line
  const modules = config.modules.filter((name) => {
    const lessFilePath = `./src/modules/${name}/${name}.less`;
    return fs.existsSync(lessFilePath);
  });

  let lessContentBundle = await fs.readFileSync(
    path.resolve(__dirname, '../../src/swiper.less'),
    'utf8',
  );
  lessContentBundle = lessContentBundle.replace(
    '//IMPORT_MODULES',
    modules.map((mod) => `@import url('./modules/${mod}/${mod}.less');`).join('\n'),
  );

  let lessContentCore = await fs.readFileSync(
    path.resolve(__dirname, '../../src/swiper.less'),
    'utf8',
  );
  lessContentCore = lessContentCore.replace('//IMPORT_MODULES', '');

  let cssStylesBundle = await autoprefixer(
    await less(lessContentBundle, path.resolve(__dirname, '../../src')),
  );
  let cssStylesCore = await autoprefixer(
    await less(lessContentCore, path.resolve(__dirname, '../../src')),
  );
  // eslint-disable-next-line
  let cssStylesSlide = getSplittedCSS(cssStylesBundle).slides;
  cssStylesBundle = getSplittedCSS(cssStylesBundle).container;
  cssStylesCore = getSplittedCSS(cssStylesCore).container;

  cssStylesBundle = proceedReplacements(cssStylesBundle);
  cssStylesCore = proceedReplacements(cssStylesCore);
  cssStylesSlide = proceedSlideReplacements(cssStylesSlide);

  cssStylesBundle = await cleanCss(cssStylesBundle);
  cssStylesCore = await cleanCss(cssStylesCore);
  cssStylesSlide = await cleanCss(cssStylesSlide);
  return {
    core: cssStylesCore,
    bundle: cssStylesBundle,
    slide: cssStylesSlide,
  };
}


import fs from 'fs-extra';

const pkg = JSON.parse(fs.readFileSync(new URL('../../package.json', import.meta.url)));
const date = {
  day: new Date().getDate(),
  month:
    'January February March April May June July August September October November December'.split(
      ' ',
    )[new Date().getMonth()],
  year: new Date().getFullYear(),
};
function banner(name = null) {
  return `${`
/**
 * Swiper ${name ? `${name} ` : ''}${pkg.version}
 * ${pkg.description}
 * ${pkg.homepage}
 *
 * Copyright 2014-${date.year} ${pkg.author}
 *
 * Released under the ${pkg.license} License
 *
 * Released on: ${date.month} ${date.day}, ${date.year}
 */
`.trim()}\n`;
}
async function addBannerToFile(file, name) {
  const content = await fs.readFile(file, 'utf-8');
  await fs.writeFile(file, `${banner(name)}\n${content}`);
}
export { banner };
export { addBannerToFile };
export default {
  banner,
  addBannerToFile,
};


import { paramsList } from './params-list.mjs';
import { isObject } from './utils.mjs';

function getChangedParams(swiperParams, oldParams, children, oldChildren, getKey) {
  const keys = [];
  if (!oldParams) return keys;
  const addKey = (key) => {
    if (keys.indexOf(key) < 0) keys.push(key);
  };
  if (children && oldChildren) {
    const oldChildrenKeys = oldChildren.map(getKey);
    const childrenKeys = children.map(getKey);
    if (oldChildrenKeys.join('') !== childrenKeys.join('')) addKey('children');
    if (oldChildren.length !== children.length) addKey('children');
  }
  const watchParams = paramsList.filter((key) => key[0] === '_').map((key) => key.replace(/_/, ''));
  watchParams.forEach((key) => {
    if (key in swiperParams && key in oldParams) {
      if (isObject(swiperParams[key]) && isObject(oldParams[key])) {
        const newKeys = Object.keys(swiperParams[key]);
        const oldKeys = Object.keys(oldParams[key]);
        if (newKeys.length !== oldKeys.length) {
          addKey(key);
        } else {
          newKeys.forEach((newKey) => {
            if (swiperParams[key][newKey] !== oldParams[key][newKey]) {
              addKey(key);
            }
          });
          oldKeys.forEach((oldKey) => {
            if (swiperParams[key][oldKey] !== oldParams[key][oldKey]) addKey(key);
          });
        }
      } else if (swiperParams[key] !== oldParams[key]) {
        addKey(key);
      }
    }
  });
  return keys;
}

export { getChangedParams };


export interface FadeEffectMethods {}

export interface FadeEffectEvents {}

export interface FadeEffectOptions {
  /**
   * Enables slides cross fade
   *
   * @default false
   */
  crossFade?: boolean;
}


imageEl.addEventListener('load', (e) => {

keep keep keep keep keep replace replace keep keep keep keep

const lazyElements = [...swiper.el.querySelectorAll('[loading="lazy"]')];

/* eslint-disable spaced-comment */
import Swiper from './swiper.mjs';
import { paramsList } from './components-shared/params-list.mjs';
import { getParams } from './components-shared/get-element-params.mjs';
import {
  needsScrollbar,
  needsNavigation,
  needsPagination,
  attrToProp,
} from './components-shared/utils.mjs';
import { updateSwiper } from './components-shared/update-swiper.mjs';

//SWIPER_STYLES
//SWIPER_SLIDE_STYLES

class DummyHTMLElement {}

const ClassToExtend =
  typeof window === 'undefined' || typeof HTMLElement === 'undefined'
    ? DummyHTMLElement
    : HTMLElement;

const arrowSvg = `<svg width="11" height="20" viewBox="0 0 11 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0.38296 20.0762C0.111788 19.805 0.111788 19.3654 0.38296 19.0942L9.19758 10.2796L0.38296 1.46497C0.111788 1.19379 0.111788 0.754138 0.38296 0.482966C0.654131 0.211794 1.09379 0.211794 1.36496 0.482966L10.4341 9.55214C10.8359 9.9539 10.8359 10.6053 10.4341 11.007L1.36496 20.0762C1.09379 20.3474 0.654131 20.3474 0.38296 20.0762Z" fill="currentColor"/></svg>
    `;

const addStyle = (shadowRoot, styles) => {
  if (typeof CSSStyleSheet !== 'undefined' && shadowRoot.adoptedStyleSheets) {
    const styleSheet = new CSSStyleSheet();
    styleSheet.replaceSync(styles);
    shadowRoot.adoptedStyleSheets = [styleSheet];
  } else {
    const style = document.createElement('style');
    style.rel = 'stylesheet';
    style.textContent = styles;
    shadowRoot.appendChild(style);
  }
};

class SwiperContainer extends ClassToExtend {
  constructor() {
    super();

    this.attachShadow({ mode: 'open' });
  }

  static get nextButtonSvg() {
    return arrowSvg;
  }

  static get prevButtonSvg() {
    return arrowSvg.replace(
      '/></svg>',
      ' transform-origin="center" transform="rotate(180)"/></svg>',
    );
  }

  cssStyles() {
    return [
      SwiperCSS, // eslint-disable-line
      ...(this.injectStyles && Array.isArray(this.injectStyles) ? this.injectStyles : []),
    ].join('\n');
  }

  cssLinks() {
    return this.injectStylesUrls || [];
  }

  calcSlideSlots() {
    const currentSideSlots = this.slideSlots || 0;
    // slide slots
    const slideSlotChildren = [...this.querySelectorAll(`[slot^=slide-]`)].map((child) => {
      return parseInt(child.getAttribute('slot').split('slide-')[1], 10);
    });
    this.slideSlots = slideSlotChildren.length ? Math.max(...slideSlotChildren) + 1 : 0;
    if (!this.rendered) return;
    if (this.slideSlots > currentSideSlots) {
      for (let i = currentSideSlots; i < this.slideSlots; i += 1) {
        const slideEl = document.createElement('swiper-slide');
        slideEl.setAttribute('part', `slide slide-${i + 1}`);
        const slotEl = document.createElement('slot');
        slotEl.setAttribute('name', `slide-${i + 1}`);
        slideEl.appendChild(slotEl);
        this.shadowRoot.querySelector('.swiper-wrapper').appendChild(slideEl);
      }
    } else if (this.slideSlots < currentSideSlots) {
      const slides = this.swiper.slides;
      for (let i = slides.length - 1; i >= 0; i -= 1) {
        if (i > this.slideSlots) {
          slides[i].remove();
        }
      }
    }
  }

  render() {
    if (this.rendered) return;

    this.calcSlideSlots();

    // local styles
    let localStyles = this.cssStyles();
    if (this.slideSlots > 0) {
      localStyles = localStyles.replace(/::slotted\(([a-z-0-9.]*)\)/g, '$1');
    }
    if (localStyles.length) {
      addStyle(this.shadowRoot, localStyles);
    }

    this.cssLinks().forEach((url) => {
      const linkExists = this.shadowRoot.querySelector(`link[href="${url}"]`);
      if (linkExists) return;
      const linkEl = document.createElement('link');
      linkEl.rel = 'stylesheet';
      linkEl.href = url;
      this.shadowRoot.appendChild(linkEl);
    });
    // prettier-ignore
    const el = document.createElement('div');
    el.classList.add('swiper');
    el.part = 'container';

    // prettier-ignore
    el.innerHTML = `
      <slot name="container-start"></slot>

        <slot></slot>
        ${Array.from({length: this.slideSlots}).map((_, index) => `
        <swiper-slide part="slide slide-${index}">
          <slot name="slide-${index}"></slot>
        </swiper-slide>
        `).join('')}

      <slot name="container-end"></slot>
      ${needsNavigation(this.passedParams) ? `

${this.constructor.prevButtonSvg}

${this.constructor.nextButtonSvg}

      ` : ''}
      ${needsPagination(this.passedParams) ? `

      ` : ''}
      ${needsScrollbar(this.passedParams) ? `

      ` : ''}
    `;
    this.shadowRoot.appendChild(el);
    this.rendered = true;
  }

  initialize() {
    if (this.initialized) return;
    this.initialized = true;
    const { params: swiperParams, passedParams } = getParams(this);
    this.swiperParams = swiperParams;
    this.passedParams = passedParams;
    delete this.swiperParams.init;

    this.render();

    // eslint-disable-next-line
    this.swiper = new Swiper(this.shadowRoot.querySelector('.swiper'), {
      ...(swiperParams.virtual
        ? {}
        : { observer: true, observeSlideChildren: this.slideSlots > 0 }),
      ...swiperParams,
      touchEventsTarget: 'container',
      onAny: (name, ...args) => {
        if (name === 'observerUpdate') {
          this.calcSlideSlots();
        }
        const eventName = swiperParams.eventsPrefix
          ? `${swiperParams.eventsPrefix}${name.toLowerCase()}`
          : name.toLowerCase();
        const event = new CustomEvent(eventName, {
          detail: args,
          bubbles: true,
          cancelable: true,
        });
        this.dispatchEvent(event);
      },
    });
  }

  connectedCallback() {
    if (
      this.initialized &&
      this.nested &&
      this.closest('swiper-slide') &&
      this.closest('swiper-slide').swiperLoopMoveDOM
    ) {
      return;
    }
    if (this.init === false || this.getAttribute('init') === 'false') {
      return;
    }
    this.initialize();
  }

  disconnectedCallback() {
    if (
      this.nested &&
      this.closest('swiper-slide') &&
      this.closest('swiper-slide').swiperLoopMoveDOM
    ) {
      return;
    }
    if (this.swiper && this.swiper.destroy) {
      this.swiper.destroy();
    }
    this.initialized = false;
  }

  updateSwiperOnPropChange(propName, propValue) {
    const { params: swiperParams, passedParams } = getParams(this, propName, propValue);
    this.passedParams = passedParams;
    this.swiperParams = swiperParams;

    updateSwiper({
      swiper: this.swiper,
      passedParams: this.passedParams,
      changedParams: [attrToProp(propName)],
      ...(propName === 'navigation' && passedParams[propName]
        ? {
            prevEl: '.swiper-button-prev',
            nextEl: '.swiper-button-next',
          }
        : {}),
      ...(propName === 'pagination' && passedParams[propName]
        ? {
            paginationEl: '.swiper-pagination',
          }
        : {}),
      ...(propName === 'scrollbar' && passedParams[propName]
        ? {
            scrollbarEl: '.swiper-scrollbar',
          }
        : {}),
    });
  }

  attributeChangedCallback(attr, prevValue, newValue) {
    if (!this.initialized) return;
    if (prevValue === 'true' && newValue === null) {
      newValue = false;
    }
    this.updateSwiperOnPropChange(attr, newValue);
  }

  static get observedAttributes() {
    const attrs = paramsList
      .filter((param) => param.includes('_'))
      .map((param) =>
        param
          .replace(/[A-Z]/g, (v) => `-${v}`)
          .replace('_', '')
          .toLowerCase(),
      );
    return attrs;
  }
}

paramsList.forEach((paramName) => {
  if (paramName === 'init') return;
  paramName = paramName.replace('_', '');
  Object.defineProperty(SwiperContainer.prototype, paramName, {
    configurable: true,
    get() {
      return (this.passedParams || {})[paramName];
    },
    set(value) {
      if (!this.passedParams) this.passedParams = {};
      this.passedParams[paramName] = value;
      if (!this.initialized) return;
      this.updateSwiperOnPropChange(paramName);
    },
  });
});

class SwiperSlide extends ClassToExtend {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
  }

  render() {
    const lazy =
      this.lazy || this.getAttribute('lazy') === '' || this.getAttribute('lazy') === 'true';
    addStyle(this.shadowRoot, SwiperSlideCSS);
    this.shadowRoot.appendChild(document.createElement('slot'));
    if (lazy) {
      const lazyDiv = document.createElement('div');
      lazyDiv.classList.add('swiper-lazy-preloader');
      this.appendChild(lazyDiv);
    }
  }

  initialize() {
    this.render();
  }

  connectedCallback() {
    this.initialize();
  }
}

// eslint-disable-next-line
const register = () => {
  if (typeof window === 'undefined') return;
  if (!window.customElements.get('swiper-container'))
    window.customElements.define('swiper-container', SwiperContainer);
  if (!window.customElements.get('swiper-slide'))
    window.customElements.define('swiper-slide', SwiperSlide);
};

if (typeof window !== 'undefined') {
  window.SwiperElementRegisterParams = (params) => {
    paramsList.push(...params);
  };
}

//BROWSER_REGISTER

export { SwiperContainer, SwiperSlide, register };


<!DOCTYPE html>

  <meta charset="utf-8">
  <title>Swiper demo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">

Slide 1

Subtitle

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dictum mattis velit, sit amet faucibus
            felis iaculis nec. Nulla laoreet justo vitae porttitor porttitor. Suspendisse in sem justo. Integer laoreet
            magna nec elit suscipit, ac laoreet nibh euismod. Aliquam hendrerit lorem at elit facilisis rutrum. Ut at
            ullamcorper velit. Nulla ligula nisi, imperdiet ut lacinia nec, tincidunt ut libero. Aenean feugiat non eros
            quis feugiat.

Slide 2

Subtitle

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dictum mattis velit, sit amet faucibus
            felis iaculis nec. Nulla laoreet justo vitae porttitor porttitor. Suspendisse in sem justo. Integer laoreet
            magna nec elit suscipit, ac laoreet nibh euismod. Aliquam hendrerit lorem at elit facilisis rutrum. Ut at
            ullamcorper velit. Nulla ligula nisi, imperdiet ut lacinia nec, tincidunt ut libero. Aenean feugiat non eros
            quis feugiat.

Slide 3

Subtitle

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dictum mattis velit, sit amet faucibus
            felis iaculis nec. Nulla laoreet justo vitae porttitor porttitor. Suspendisse in sem justo. Integer laoreet
            magna nec elit suscipit, ac laoreet nibh euismod. Aliquam hendrerit lorem at elit facilisis rutrum. Ut at
            ullamcorper velit. Nulla ligula nisi, imperdiet ut lacinia nec, tincidunt ut libero. Aenean feugiat non eros
            quis feugiat.


<!DOCTYPE html>

  <meta charset="utf-8">
  <title>Swiper demo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">

Slide 1

Slide 2

Slide 3

Slide 4

Slide 5

Slide 6

Slide 7

Slide 8

Slide 9

Slide 10


export const processLazyPreloader = (swiper, imageEl) => {

if (!swiper || swiper.destroyed || !swiper.params) return;

const slideSelector = () => (swiper.isElement ? `swiper-slide` : `.${swiper.params.slideClass}`);

const slideEl = imageEl.closest(slideSelector());

const lazyEl = slideEl.querySelector(`.${swiper.params.lazyPreloaderClass}`);

let lazyEl = slideEl.querySelector(`.${swiper.params.lazyPreloaderClass}`);

/* eslint no-param-reassign: "off" */
import { getDocument } from 'ssr-window';
import {
  extend,
  deleteProps,
  createElement,
  elementChildren,
  elementStyle,
  elementIndex,
} from '../shared/utils.mjs';
import { getSupport } from '../shared/get-support.mjs';
import { getDevice } from '../shared/get-device.mjs';
import { getBrowser } from '../shared/get-browser.mjs';

import Resize from './modules/resize/resize.mjs';
import Observer from './modules/observer/observer.mjs';

import eventsEmitter from './events-emitter.mjs';

import update from './update/index.mjs';
import translate from './translate/index.mjs';
import transition from './transition/index.mjs';
import slide from './slide/index.mjs';
import loop from './loop/index.mjs';
import grabCursor from './grab-cursor/index.mjs';
import events from './events/index.mjs';
import breakpoints from './breakpoints/index.mjs';
import classes from './classes/index.mjs';
import checkOverflow from './check-overflow/index.mjs';

import defaults from './defaults.mjs';
import moduleExtendParams from './moduleExtendParams.mjs';
import { processLazyPreloader, preload } from '../shared/process-lazy-preloader.mjs';

const prototypes = {
  eventsEmitter,
  update,
  translate,
  transition,
  slide,
  loop,
  grabCursor,
  events,
  breakpoints,
  checkOverflow,
  classes,
};

const extendedDefaults = {};

class Swiper {
  constructor(...args) {
    let el;
    let params;
    if (
      args.length === 1 &&
      args[0].constructor &&
      Object.prototype.toString.call(args[0]).slice(8, -1) === 'Object'
    ) {
      params = args[0];
    } else {
      [el, params] = args;
    }
    if (!params) params = {};

    params = extend({}, params);
    if (el && !params.el) params.el = el;

    const document = getDocument();

    if (
      params.el &&
      typeof params.el === 'string' &&
      document.querySelectorAll(params.el).length > 1
    ) {
      const swipers = [];
      document.querySelectorAll(params.el).forEach((containerEl) => {
        const newParams = extend({}, params, { el: containerEl });
        swipers.push(new Swiper(newParams));
      });
      // eslint-disable-next-line no-constructor-return
      return swipers;
    }

    // Swiper Instance
    const swiper = this;
    swiper.__swiper__ = true;
    swiper.support = getSupport();
    swiper.device = getDevice({ userAgent: params.userAgent });
    swiper.browser = getBrowser();

    swiper.eventsListeners = {};
    swiper.eventsAnyListeners = [];
    swiper.modules = [...swiper.__modules__];
    if (params.modules && Array.isArray(params.modules)) {
      swiper.modules.push(...params.modules);
    }

    const allModulesParams = {};
    swiper.modules.forEach((mod) => {
      mod({
        params,
        swiper,
        extendParams: moduleExtendParams(params, allModulesParams),
        on: swiper.on.bind(swiper),
        once: swiper.once.bind(swiper),
        off: swiper.off.bind(swiper),
        emit: swiper.emit.bind(swiper),
      });
    });

    // Extend defaults with modules params
    const swiperParams = extend({}, defaults, allModulesParams);

    // Extend defaults with passed params
    swiper.params = extend({}, swiperParams, extendedDefaults, params);
    swiper.originalParams = extend({}, swiper.params);
    swiper.passedParams = extend({}, params);

    // add event listeners
    if (swiper.params && swiper.params.on) {
      Object.keys(swiper.params.on).forEach((eventName) => {
        swiper.on(eventName, swiper.params.on[eventName]);
      });
    }
    if (swiper.params && swiper.params.onAny) {
      swiper.onAny(swiper.params.onAny);
    }

    // Extend Swiper
    Object.assign(swiper, {
      enabled: swiper.params.enabled,
      el,

      // Classes
      classNames: [],

      // Slides
      slides: [],
      slidesGrid: [],
      snapGrid: [],
      slidesSizesGrid: [],

      // isDirection
      isHorizontal() {
        return swiper.params.direction === 'horizontal';
      },
      isVertical() {
        return swiper.params.direction === 'vertical';
      },

      // Indexes
      activeIndex: 0,
      realIndex: 0,

      //
      isBeginning: true,
      isEnd: false,

      // Props
      translate: 0,
      previousTranslate: 0,
      progress: 0,
      velocity: 0,
      animating: false,

      cssOverflowAdjustment() {
        // Returns 0 unless `translate` is > 2**23
        // Should be subtracted from css values to prevent overflow
        return Math.trunc(this.translate / 2 ** 23) * 2 ** 23;
      },

      // Locks
      allowSlideNext: swiper.params.allowSlideNext,
      allowSlidePrev: swiper.params.allowSlidePrev,

      // Touch Events
      touchEventsData: {
        isTouched: undefined,
        isMoved: undefined,
        allowTouchCallbacks: undefined,
        touchStartTime: undefined,
        isScrolling: undefined,
        currentTranslate: undefined,
        startTranslate: undefined,
        allowThresholdMove: undefined,
        // Form elements to match
        focusableElements: swiper.params.focusableElements,
        // Last click time
        lastClickTime: 0,
        clickTimeout: undefined,
        // Velocities
        velocities: [],
        allowMomentumBounce: undefined,
        startMoving: undefined,
        evCache: [],
      },

      // Clicks
      allowClick: true,

      // Touches
      allowTouchMove: swiper.params.allowTouchMove,

      touches: {
        startX: 0,
        startY: 0,
        currentX: 0,
        currentY: 0,
        diff: 0,
      },

      // Images
      imagesToLoad: [],
      imagesLoaded: 0,
    });

    swiper.emit('_swiper');

    // Init
    if (swiper.params.init) {
      swiper.init();
    }

    // Return app instance
    // eslint-disable-next-line no-constructor-return
    return swiper;
  }

  getSlideIndex(slideEl) {
    const { slidesEl, params } = this;
    const slides = elementChildren(slidesEl, `.${params.slideClass}, swiper-slide`);
    const firstSlideIndex = elementIndex(slides[0]);
    return elementIndex(slideEl) - firstSlideIndex;
  }

  getSlideIndexByData(index) {
    return this.getSlideIndex(
      this.slides.filter(
        (slideEl) => slideEl.getAttribute('data-swiper-slide-index') * 1 === index,
      )[0],
    );
  }

  recalcSlides() {
    const swiper = this;
    const { slidesEl, params } = swiper;
    swiper.slides = elementChildren(slidesEl, `.${params.slideClass}, swiper-slide`);
  }

  enable() {
    const swiper = this;
    if (swiper.enabled) return;
    swiper.enabled = true;
    if (swiper.params.grabCursor) {
      swiper.setGrabCursor();
    }
    swiper.emit('enable');
  }

  disable() {
    const swiper = this;
    if (!swiper.enabled) return;
    swiper.enabled = false;
    if (swiper.params.grabCursor) {
      swiper.unsetGrabCursor();
    }
    swiper.emit('disable');
  }

  setProgress(progress, speed) {
    const swiper = this;
    progress = Math.min(Math.max(progress, 0), 1);
    const min = swiper.minTranslate();
    const max = swiper.maxTranslate();
    const current = (max - min) * progress + min;
    swiper.translateTo(current, typeof speed === 'undefined' ? 0 : speed);
    swiper.updateActiveIndex();
    swiper.updateSlidesClasses();
  }

  emitContainerClasses() {
    const swiper = this;
    if (!swiper.params._emitClasses || !swiper.el) return;
    const cls = swiper.el.className.split(' ').filter((className) => {
      return (
        className.indexOf('swiper') === 0 ||
        className.indexOf(swiper.params.containerModifierClass) === 0
      );
    });
    swiper.emit('_containerClasses', cls.join(' '));
  }

  getSlideClasses(slideEl) {
    const swiper = this;
    if (swiper.destroyed) return '';

    return slideEl.className
      .split(' ')
      .filter((className) => {
        return (
          className.indexOf('swiper-slide') === 0 ||
          className.indexOf(swiper.params.slideClass) === 0
        );
      })
      .join(' ');
  }

  emitSlidesClasses() {
    const swiper = this;
    if (!swiper.params._emitClasses || !swiper.el) return;
    const updates = [];
    swiper.slides.forEach((slideEl) => {
      const classNames = swiper.getSlideClasses(slideEl);
      updates.push({ slideEl, classNames });
      swiper.emit('_slideClass', slideEl, classNames);
    });
    swiper.emit('_slideClasses', updates);
  }

  slidesPerViewDynamic(view = 'current', exact = false) {
    const swiper = this;
    const { params, slides, slidesGrid, slidesSizesGrid, size: swiperSize, activeIndex } = swiper;
    let spv = 1;

    if (params.centeredSlides) {
      let slideSize = slides[activeIndex] ? slides[activeIndex].swiperSlideSize : 0;
      let breakLoop;
      for (let i = activeIndex + 1; i < slides.length; i += 1) {
        if (slides[i] && !breakLoop) {
          slideSize += slides[i].swiperSlideSize;
          spv += 1;
          if (slideSize > swiperSize) breakLoop = true;
        }
      }
      for (let i = activeIndex - 1; i >= 0; i -= 1) {
        if (slides[i] && !breakLoop) {
          slideSize += slides[i].swiperSlideSize;
          spv += 1;
          if (slideSize > swiperSize) breakLoop = true;
        }
      }
    } else {
      // eslint-disable-next-line
      if (view === 'current') {
        for (let i = activeIndex + 1; i < slides.length; i += 1) {
          const slideInView = exact
            ? slidesGrid[i] + slidesSizesGrid[i] - slidesGrid[activeIndex] < swiperSize
            : slidesGrid[i] - slidesGrid[activeIndex] < swiperSize;
          if (slideInView) {
            spv += 1;
          }
        }
      } else {
        // previous
        for (let i = activeIndex - 1; i >= 0; i -= 1) {
          const slideInView = slidesGrid[activeIndex] - slidesGrid[i] < swiperSize;
          if (slideInView) {
            spv += 1;
          }
        }
      }
    }
    return spv;
  }

  update() {
    const swiper = this;
    if (!swiper || swiper.destroyed) return;
    const { snapGrid, params } = swiper;
    // Breakpoints
    if (params.breakpoints) {
      swiper.setBreakpoint();
    }

    [...swiper.el.querySelectorAll('[loading="lazy"]')].forEach((imageEl) => {
      if (imageEl.complete) {
        processLazyPreloader(swiper, imageEl);
      }
    });

    swiper.updateSize();
    swiper.updateSlides();
    swiper.updateProgress();
    swiper.updateSlidesClasses();

    function setTranslate() {
      const translateValue = swiper.rtlTranslate ? swiper.translate * -1 : swiper.translate;
      const newTranslate = Math.min(
        Math.max(translateValue, swiper.maxTranslate()),
        swiper.minTranslate(),
      );
      swiper.setTranslate(newTranslate);
      swiper.updateActiveIndex();
      swiper.updateSlidesClasses();
    }
    let translated;
    if (params.freeMode && params.freeMode.enabled && !params.cssMode) {
      setTranslate();
      if (params.autoHeight) {
        swiper.updateAutoHeight();
      }
    } else {
      if (
        (params.slidesPerView === 'auto' || params.slidesPerView > 1) &&
        swiper.isEnd &&
        !params.centeredSlides
      ) {
        const slides =
          swiper.virtual && params.virtual.enabled ? swiper.virtual.slides : swiper.slides;
        translated = swiper.slideTo(slides.length - 1, 0, false, true);
      } else {
        translated = swiper.slideTo(swiper.activeIndex, 0, false, true);
      }
      if (!translated) {
        setTranslate();
      }
    }
    if (params.watchOverflow && snapGrid !== swiper.snapGrid) {
      swiper.checkOverflow();
    }
    swiper.emit('update');
  }

  changeDirection(newDirection, needUpdate = true) {
    const swiper = this;
    const currentDirection = swiper.params.direction;
    if (!newDirection) {
      // eslint-disable-next-line
      newDirection = currentDirection === 'horizontal' ? 'vertical' : 'horizontal';
    }
    if (
      newDirection === currentDirection ||
      (newDirection !== 'horizontal' && newDirection !== 'vertical')
    ) {
      return swiper;
    }

    swiper.el.classList.remove(`${swiper.params.containerModifierClass}${currentDirection}`);
    swiper.el.classList.add(`${swiper.params.containerModifierClass}${newDirection}`);
    swiper.emitContainerClasses();

    swiper.params.direction = newDirection;

    swiper.slides.forEach((slideEl) => {
      if (newDirection === 'vertical') {
        slideEl.style.width = '';
      } else {
        slideEl.style.height = '';
      }
    });

    swiper.emit('changeDirection');
    if (needUpdate) swiper.update();

    return swiper;
  }

  changeLanguageDirection(direction) {
    const swiper = this;
    if ((swiper.rtl && direction === 'rtl') || (!swiper.rtl && direction === 'ltr')) return;
    swiper.rtl = direction === 'rtl';
    swiper.rtlTranslate = swiper.params.direction === 'horizontal' && swiper.rtl;
    if (swiper.rtl) {
      swiper.el.classList.add(`${swiper.params.containerModifierClass}rtl`);
      swiper.el.dir = 'rtl';
    } else {
      swiper.el.classList.remove(`${swiper.params.containerModifierClass}rtl`);
      swiper.el.dir = 'ltr';
    }
    swiper.update();
  }

  mount(element) {
    const swiper = this;
    if (swiper.mounted) return true;

    // Find el
    let el = element || swiper.params.el;
    if (typeof el === 'string') {
      el = document.querySelector(el);
    }
    if (!el) {
      return false;
    }

    el.swiper = swiper;
    if (el.parentNode && el.parentNode.host && el.parentNode.host.nodeName === 'SWIPER-CONTAINER') {
      swiper.isElement = true;
    }

    const getWrapperSelector = () => {
      return `.${(swiper.params.wrapperClass || '').trim().split(' ').join('.')}`;
    };

    const getWrapper = () => {
      if (el && el.shadowRoot && el.shadowRoot.querySelector) {
        const res = el.shadowRoot.querySelector(getWrapperSelector());
        // Children needs to return slot items
        return res;
      }
      return elementChildren(el, getWrapperSelector())[0];
    };
    // Find Wrapper
    let wrapperEl = getWrapper();
    if (!wrapperEl && swiper.params.createElements) {
      wrapperEl = createElement('div', swiper.params.wrapperClass);
      el.append(wrapperEl);
      elementChildren(el, `.${swiper.params.slideClass}`).forEach((slideEl) => {
        wrapperEl.append(slideEl);
      });
    }

    Object.assign(swiper, {
      el,
      wrapperEl,
      slidesEl: swiper.isElement && !el.parentNode.host.slideSlots ? el.parentNode.host : wrapperEl,
      hostEl: swiper.isElement ? el.parentNode.host : el,
      mounted: true,

      // RTL
      rtl: el.dir.toLowerCase() === 'rtl' || elementStyle(el, 'direction') === 'rtl',
      rtlTranslate:
        swiper.params.direction === 'horizontal' &&
        (el.dir.toLowerCase() === 'rtl' || elementStyle(el, 'direction') === 'rtl'),
      wrongRTL: elementStyle(wrapperEl, 'display') === '-webkit-box',
    });

    return true;
  }

  init(el) {
    const swiper = this;
    if (swiper.initialized) return swiper;

    const mounted = swiper.mount(el);
    if (mounted === false) return swiper;

    swiper.emit('beforeInit');

    // Set breakpoint
    if (swiper.params.breakpoints) {
      swiper.setBreakpoint();
    }

    // Add Classes
    swiper.addClasses();

    // Update size
    swiper.updateSize();

    // Update slides
    swiper.updateSlides();

    if (swiper.params.watchOverflow) {
      swiper.checkOverflow();
    }

    // Set Grab Cursor
    if (swiper.params.grabCursor && swiper.enabled) {
      swiper.setGrabCursor();
    }

    // Slide To Initial Slide
    if (swiper.params.loop && swiper.virtual && swiper.params.virtual.enabled) {
      swiper.slideTo(
        swiper.params.initialSlide + swiper.virtual.slidesBefore,
        0,
        swiper.params.runCallbacksOnInit,
        false,
        true,
      );
    } else {
      swiper.slideTo(swiper.params.initialSlide, 0, swiper.params.runCallbacksOnInit, false, true);
    }

    // Create loop
    if (swiper.params.loop) {
      swiper.loopCreate();
    }

    // Attach events
    swiper.attachEvents();

    [...swiper.el.querySelectorAll('[loading="lazy"]')].forEach((imageEl) => {
      if (imageEl.complete) {
        processLazyPreloader(swiper, imageEl);
      } else {
        imageEl.addEventListener('load', (e) => {
          processLazyPreloader(swiper, e.target);
        });
      }
    });
    preload(swiper);

    // Init Flag
    swiper.initialized = true;

    preload(swiper);

    // Emit
    swiper.emit('init');
    swiper.emit('afterInit');

    return swiper;
  }

  destroy(deleteInstance = true, cleanStyles = true) {
    const swiper = this;
    const { params, el, wrapperEl, slides } = swiper;

    if (typeof swiper.params === 'undefined' || swiper.destroyed) {
      return null;
    }

    swiper.emit('beforeDestroy');

    // Init Flag
    swiper.initialized = false;

    // Detach events
    swiper.detachEvents();

    // Destroy loop
    if (params.loop) {
      swiper.loopDestroy();
    }

    // Cleanup styles
    if (cleanStyles) {
      swiper.removeClasses();
      el.removeAttribute('style');
      wrapperEl.removeAttribute('style');
      if (slides && slides.length) {
        slides.forEach((slideEl) => {
          slideEl.classList.remove(
            params.slideVisibleClass,
            params.slideActiveClass,
            params.slideNextClass,
            params.slidePrevClass,
          );
          slideEl.removeAttribute('style');
          slideEl.removeAttribute('data-swiper-slide-index');
        });
      }
    }

    swiper.emit('destroy');

    // Detach emitter events
    Object.keys(swiper.eventsListeners).forEach((eventName) => {
      swiper.off(eventName);
    });

    if (deleteInstance !== false) {
      swiper.el.swiper = null;
      deleteProps(swiper);
    }
    swiper.destroyed = true;

    return null;
  }

  static extendDefaults(newDefaults) {
    extend(extendedDefaults, newDefaults);
  }

  static get extendedDefaults() {
    return extendedDefaults;
  }

  static get defaults() {
    return defaults;
  }

  static installModule(mod) {
    if (!Swiper.prototype.__modules__) Swiper.prototype.__modules__ = [];
    const modules = Swiper.prototype.__modules__;

    if (typeof mod === 'function' && modules.indexOf(mod) < 0) {
      modules.push(mod);
    }
  }

  static use(module) {
    if (Array.isArray(module)) {
      module.forEach((m) => Swiper.installModule(m));
      return Swiper;
    }
    Swiper.installModule(module);
    return Swiper;
  }
}

Object.keys(prototypes).forEach((prototypeGroup) => {
  Object.keys(prototypes[prototypeGroup]).forEach((protoMethod) => {
    Swiper.prototype[protoMethod] = prototypes[prototypeGroup][protoMethod];
  });
});

Swiper.use([Resize, Observer]);

export default Swiper;


function checkOverflow() {
  const swiper = this;
  const { isLocked: wasLocked, params } = swiper;
  const { slidesOffsetBefore } = params;

  if (slidesOffsetBefore) {
    const lastSlideIndex = swiper.slides.length - 1;
    const lastSlideRightEdge =
      swiper.slidesGrid[lastSlideIndex] +
      swiper.slidesSizesGrid[lastSlideIndex] +
      slidesOffsetBefore * 2;
    swiper.isLocked = swiper.size > lastSlideRightEdge;
  } else {
    swiper.isLocked = swiper.snapGrid.length === 1;
  }
  if (params.allowSlideNext === true) {
    swiper.allowSlideNext = !swiper.isLocked;
  }
  if (params.allowSlidePrev === true) {
    swiper.allowSlidePrev = !swiper.isLocked;
  }

  if (wasLocked && wasLocked !== swiper.isLocked) {
    swiper.isEnd = false;
  }
  if (wasLocked !== swiper.isLocked) {
    swiper.emit(swiper.isLocked ? 'lock' : 'unlock');
  }
}

export default { checkOverflow };


// @ts-ignore
import Swiper from './types/swiper-class.d.ts';

export default Swiper;
export { Swiper };


const lazyDiv = document.createElement('div');

keep keep keep replace keep keep keep keep keep


@at-root {
  :root {
    /*
  --swiper-scrollbar-border-radius: 10px;
  --swiper-scrollbar-top: auto;
  --swiper-scrollbar-bottom: 4px;
  --swiper-scrollbar-left: auto;
  --swiper-scrollbar-right: 4px;
  --swiper-scrollbar-sides-offset: 1%;
  --swiper-scrollbar-bg-color: rgba(0, 0, 0, 0.1);
  --swiper-scrollbar-drag-bg-color: rgba(0, 0, 0, 0.5);
  --swiper-scrollbar-size: 4px;
  */
  }
}
.swiper-scrollbar {
  border-radius: var(--swiper-scrollbar-border-radius, 10px);
  position: relative;
  -ms-touch-action: none;
  background: var(--swiper-scrollbar-bg-color, rgba(0, 0, 0, 0.1));
  .swiper-scrollbar-disabled > &,
  &.swiper-scrollbar-disabled {
    display: none !important;
  }
  .swiper-horizontal > &,
  &.swiper-scrollbar-horizontal {
    position: absolute;
    left: var(--swiper-scrollbar-sides-offset, 1%);
    bottom: var(--swiper-scrollbar-bottom, 4px);
    top: var(--swiper-scrollbar-top, auto);
    z-index: 50;
    height: var(--swiper-scrollbar-size, 4px);
    width: calc(100% - 2 * var(--swiper-scrollbar-sides-offset, 1%));
  }
  .swiper-vertical > &,
  &.swiper-scrollbar-vertical {
    position: absolute;
    left: var(--swiper-scrollbar-left, auto);
    right: var(--swiper-scrollbar-right, 4px);
    top: var(--swiper-scrollbar-sides-offset, 1%);
    z-index: 50;
    width: var(--swiper-scrollbar-size, 4px);
    height: calc(100% - 2 * var(--swiper-scrollbar-sides-offset, 1%));
  }
}
.swiper-scrollbar-drag {
  height: 100%;
  width: 100%;
  position: relative;
  background: var(--swiper-scrollbar-drag-bg-color, rgba(0, 0, 0, 0.5));
  border-radius: var(--swiper-scrollbar-border-radius, 10px);
  left: 0;
  top: 0;
}
.swiper-scrollbar-cursor-drag {
  cursor: move;
}
.swiper-scrollbar-lock {
  display: none;
}


export default function removeAllSlides() {
  const swiper = this;

  const slidesIndexes = [];
  for (let i = 0; i < swiper.slides.length; i += 1) {
    slidesIndexes.push(i);
  }
  swiper.removeSlide(slidesIndexes);
}