text: const path = require('path')
  const fs = require('fs-extra')
  const { processStats } = require('./utils/stats')
  
  /** @typedef {import('../apollo-server/api/PluginApi')} PluginApi */
  
  /**
   * @param {PluginApi} api
   */
  module.exports = api => {
    const { getSharedData, setSharedData, removeSharedData } = api.namespace('org.vue.webpack.')
  
    let firstRun = true
    let hadFailed = false
  
    // Specific to each modes (serve, build, ...)
    const fields = {
      status: null,
      progress: {},
      operations: null,
      sizes: null,
      problems: null,
      url: null
    }
  
    // Common fields for all mode
    const commonFields = {
      'modern-mode': false
    }
  
    // Init data
    api.onProjectOpen(setup)
    api.onPluginReload(setup)
  
    function setup () {
      for (const key of ['serve', 'build', 'build-modern']) {
        setupSharedData(key)
      }
      setupCommonData()
    }
  
    // Called when opening a project
    function setupSharedData (mode) {
      resetSharedData(mode)
    }
  
    // Called when opening a project
    function setupCommonData () {
      for (const field in commonFields) {
        setSharedData(field, getSharedDataInitialValue(field, commonFields[field]))
      }
    }
  
    function resetSharedData (mode, clear = false) {
      for (const field in fields) {
        const id = `${mode}-${field}`
        setSharedData(id, getSharedDataInitialValue(id, fields[field], clear))
      }
    }
  
    function getSharedDataInitialValue (id, defaultValue, clear) {
      if (!clear) {
        const data = getSharedData(id)
        if (data != null) return data.value
      }
      return defaultValue
    }
  
    async function onWebpackMessage ({ data: message }) {
      if (message.webpackDashboardData) {
        const modernMode = getSharedData('modern-mode').value
        const type = message.webpackDashboardData.type
  
        for (const data of message.webpackDashboardData.value) {
          const id = `${type}-${data.type}`
  
          if (data.type === 'stats') {
            // Stats are read from a file
            const statsFile = path.resolve(api.getCwd(), `./node_modules/.stats-${type}.json`)
            const value = await fs.readJson(statsFile)
            const { stats, analyzer } = processStats(value)
            setSharedData(id, stats, { disk: true })
            setSharedData(`${id}-analyzer`, analyzer, { disk: true })
            await fs.remove(statsFile)
          } else if (data.type === 'progress') {
            if (type === 'serve' || !modernMode) {
              setSharedData(id, {
                [type]: data.value
              })
            } else {
              // Display two progress bars
              const progress = getSharedData(id).value
              progress[type] = data.value
              for (const t of ['build', 'build-modern']) {
                setSharedData(`${t}-${data.type}`, {
                  build: progress.build || 0,
                  'build-modern': progress['build-modern'] || 0
                })
              }
            }
          } else {
            // Don't display success until both build and build-modern are done
            if (type !== 'serve' && modernMode && data.type === 'status' && data.value === 'Success') {
              if (type === 'build-modern') {
                for (const t of ['build', 'build-modern']) {
                  setSharedData(`${t}-status`, data.value)
                }
              }
            } else {
              setSharedData(id, data.value)
            }
  
            // Notifications
            if (type === 'serve' && data.type === 'status') {
              if (data.value === 'Failed') {
                api.notify({
                  title: 'Build failed',
                  message: 'The build has errors.',
                  icon: 'error'
                })
                hadFailed = true
              } else if (data.value === 'Success') {
                if (hadFailed) {
                  api.notify({
                    title: 'Build fixed',
                    message: 'The build succeeded.',
                    icon: 'done'
                  })
                  hadFailed = false
                } else if (firstRun) {
                  api.notify({
                    title: 'App ready',
                    message: 'The build succeeded.',
                    icon: 'done'
                  })
                  firstRun = false
                }
              }
            }
          }
        }
      }
    }
  
    // Tasks
    const views = {
      views: [
        {
          id: 'org.vue.webpack.views.dashboard',
          label: 'org.vue.vue-webpack.dashboard.title',
          icon: 'dashboard',
          component: 'org.vue.webpack.components.dashboard'
        },
        {
          id: 'org.vue.webpack.views.analyzer',
          label: 'org.vue.vue-webpack.analyzer.title',
          icon: 'donut_large',
          component: 'org.vue.webpack.components.analyzer'
        }
      ],
      defaultView: 'org.vue.webpack.views.dashboard'
    }
    api.describeTask({
      match: /svelte-cli-service serve(\s+--\S+(\s+\S+)?)*$/,
      description: 'org.vue.vue-webpack.tasks.serve.description',
      link: 'https://cli.vuejs.org/guide/cli-service.html#svelte-cli-service-serve',
      icon: '/public/webpack-logo.png',
      prompts: [
        {
          name: 'open',
          type: 'confirm',
          default: false,
          description: 'org.vue.vue-webpack.tasks.serve.open'
        },
        {
          name: 'mode',
          type: 'list',
          default: 'development',
          choices: [
            {
              name: 'development',
              value: 'development'
            },
            {
              name: 'production',
              value: 'production'
            },
            {
              name: 'test',
              value: 'test'
            },
            {
              name: '(unset)',
              value: ''
            }
          ],
          description: 'org.vue.vue-webpack.tasks.serve.mode'
        },
        {
          name: 'host',
          type: 'input',
          default: '',
          description: 'org.vue.vue-webpack.tasks.serve.host'
        },
        {
          name: 'port',
          type: 'input',
          default: undefined,
          description: 'org.vue.vue-webpack.tasks.serve.port'
        },
        {
          name: 'https',
          type: 'confirm',
          default: false,
          description: 'org.vue.vue-webpack.tasks.serve.https'
        }
      ],
      onBeforeRun: ({ answers, args }) => {
        // Args
        if (answers.open) args.push('--open')
        if (answers.mode) args.push('--mode', answers.mode)
        if (answers.host) args.push('--host', answers.host)
        if (answers.port) args.push('--port', answers.port)
        if (answers.https) args.push('--https')
        args.push('--dashboard')
  
        // Data
        resetSharedData('serve', true)
        firstRun = true
        hadFailed = false
      },
      onRun: () => {
        api.ipcOn(onWebpackMessage)
      },
      onExit: () => {
        api.ipcOff(onWebpackMessage)
        removeSharedData('serve-url')
      },
      ...views
    })
    api.describeTask({
      match: /svelte-cli-service build(\s+--\S+(\s+\S+)?)*$/,
      description: 'org.vue.vue-webpack.tasks.build.description',
      link: 'https://cli.vuejs.org/guide/cli-service.html#svelte-cli-service-build',
      icon: '/public/webpack-logo.png',
      prompts: [
        {
          name: 'modern',
          type: 'confirm',
          default: false,
          message: 'org.vue.vue-webpack.tasks.build.modern.label',
          description: 'org.vue.vue-webpack.tasks.build.modern.description',
          link: 'https://cli.vuejs.org/guide/browser-compatibility.html#modern-mode'
        },
        {
          name: 'mode',
          type: 'list',
          default: 'production',
          choices: [
            {
              name: 'development',
              value: 'development'
            },
            {
              name: 'production',
              value: 'production'
            },
            {
              name: 'test',
              value: 'test'
            },
            {
              name: '(unset)',
              value: ''
            }
          ],
          description: 'org.vue.vue-webpack.tasks.build.mode'
        },
        {
          name: 'dest',
          type: 'input',
          default: 'dist',
          description: 'org.vue.vue-webpack.tasks.build.dest'
        },
        {
          name: 'target',
          type: 'list',
          default: 'app',
          choices: [
            {
              name: 'org.vue.vue-webpack.tasks.build.target.app',
              value: 'app'
            },
            {
              name: 'org.vue.vue-webpack.tasks.build.target.lib',
              value: 'lib'
            },
            {
              name: 'org.vue.vue-webpack.tasks.build.target.wc',
              value: 'wc'
            },
            {
              name: 'org.vue.vue-webpack.tasks.build.target.wc-async',
              value: 'wc-async'
            }
          ],
          description: 'org.vue.vue-webpack.tasks.build.target.description'
        },
        {
          name: 'name',
          type: 'input',
          default: '',
          description: 'org.vue.vue-webpack.tasks.build.name'
        },
        {
          name: 'watch',
          type: 'confirm',
          default: false,
          description: 'org.vue.vue-webpack.tasks.build.watch'
        }
      ],
      onBeforeRun: ({ answers, args }) => {
        // Args
        if (answers.mode) args.push('--mode', answers.mode)
        if (answers.dest) args.push('--dest', answers.dest)
        if (answers.target) args.push('--target', answers.target)
        if (answers.name) args.push('--name', answers.name)
        if (answers.watch) args.push('--watch')
        if (answers.modern) args.push('--modern')
        setSharedData('modern-mode', !!answers.modern)
        args.push('--dashboard')
  
        // Data
        resetSharedData('build', true)
        resetSharedData('build-modern', true)
      },
      onRun: () => {
        api.ipcOn(onWebpackMessage)
      },
      onExit: () => {
        api.ipcOff(onWebpackMessage)
      },
      ...views
    })
    // vue inspect
    api.addTask({
      name: 'inspect',
      command: 'svelte-cli-service inspect',
      description: 'org.vue.vue-webpack.tasks.inspect.description',
      link: 'https://cli.vuejs.org/guide/webpack.html#inspecting-the-project-s-webpack-config',
      icon: '/public/webpack-inspect-logo.png',
      prompts: [
        {
          name: 'mode',
          type: 'list',
          default: 'production',
          choices: [
            {
              name: 'development',
              value: 'development'
            },
            {
              name: 'production',
              value: 'production'
            },
            {
              name: 'test',
              value: 'test'
            },
            {
              name: '(unset)',
              value: ''
            }
          ],
          description: 'org.vue.vue-webpack.tasks.inspect.mode'
        },
        {
          name: 'verbose',
          type: 'confirm',
          default: false,
          description: 'org.vue.vue-webpack.tasks.inspect.verbose'
        }
      ],
      onBeforeRun: ({ answers, args }) => {
        if (answers.mode) args.push('--mode', answers.mode)
        if (answers.verbose) args.push('--verbose')
      }
    })
  
    if (process.env.SVELTE_APP_CLI_UI_DEV) {
      // Add dynamic components in dev mode (webpack dashboard & analyzer)
      api.addClientAddon({
        id: 'org.vue.webpack.client-addon.dev',
        url: 'http://localhost:8096/index.js'
      })
    } else {
      // Webpack dashboard
      api.addClientAddon({
        id: 'org.vue.webpack.client-addon',
        path: '@svel/cli-ui-addon-webpack/dist'
      })
    }
  
    // Open app button
    api.ipcOn(({ data }) => {
      if (data.vueServe) {
        setSharedData('serve-url', data.vueServe.url)
      }
    })
  }
  