'use strict';

var node_fs = require('node:fs');
var node_path = require('node:path');
var node_module = require('node:module');
var vite = require('vite');

var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));

const quasarPath = node_path.dirname(require$1.resolve('quasar/package.json'));

const { version } = JSON.parse(
  node_fs.readFileSync(node_path.join(quasarPath, 'package.json'), 'utf-8')
);

function getViteConfig (runMode, viteMode, externalViteCfg) {
  const viteCfg = {
    define: {
      __QUASAR_VERSION__: `'${ version }'`,
      __QUASAR_SSR__: false,
      __QUASAR_SSR_SERVER__: false,
      __QUASAR_SSR_CLIENT__: false
    },

    css: {
      preprocessorOptions: {
        // Use sass-embedded for better stability and performance
        sass: {
          api: 'modern-compiler',
          silenceDeprecations: [ 'import', 'global-builtin' ]
        },
        scss: {
          api: 'modern-compiler',
          silenceDeprecations: [ 'import', 'global-builtin' ]
        }
      }
    }
  };

  // Set this to the default value only if it's not already set.
  // @quasar/app-vite configures this by itself when it needs it.
  if (!externalViteCfg.define || externalViteCfg.define.__QUASAR_SSR_PWA__ === void 0) {
    viteCfg.define.__QUASAR_SSR_PWA__ = false;
  }

  if (runMode === 'ssr-server') {
    Object.assign(viteCfg.define, {
      __QUASAR_SSR__: true,
      __QUASAR_SSR_SERVER__: true
    });
  }
  else {
    // Alias "quasar" package to its dev file (which has flags)
    // to reduce the number of HTTP requests while in DEV mode
    if (viteMode !== 'production') {
      viteCfg.resolve = {
        alias: [
          { find: /^quasar$/, replacement: 'quasar/dist/quasar.client.js' }
        ]
      };
    }
    else {
      viteCfg.optimizeDeps = {
        exclude: [ 'quasar' ]
      };
    }

    if (runMode === 'ssr-client') {
      Object.assign(viteCfg.define, {
        __QUASAR_SSR__: true,
        __QUASAR_SSR_CLIENT__: true
      });
    }
  }

  return viteCfg
}

let quasarImportMap;
function loadQuasarImportMap () {
  if (quasarImportMap !== void 0) return

  try {
    quasarImportMap = JSON.parse(
      node_fs.readFileSync(
        node_path.join(quasarPath, 'dist/transforms/import-map.json'),
        'utf-8'
      )
    );
  }
  catch (error) {
    throw new Error('Failed to load Quasar import map', { cause: error })
  }
}

const importQuasarRegex = /import\s*\{([\w,\s]+)\}\s*from\s*(['"])quasar\2;?/g;

function importTransformation (importName) {
  const file = quasarImportMap[ importName ];
  if (file === void 0) {
    throw new Error('Unknown import from Quasar: ' + importName)
  }
  return 'quasar/' + file
}

/**
 * Transforms JS code where importing from 'quasar' package
 * Example:
 *       import { QBtn } from 'quasar'
 *    -> import QBtn from 'quasar/src/components/QBtn.js'
 */
function mapQuasarImports (code, importMap = {}) {
  return code.replace(
    importQuasarRegex,
    (_, match) => match
      .split(',')
      .map(identifier => {
        const data = identifier.split(' as ');
        const importName = data[ 0 ].trim();

        // might be an empty entry like below
        // (notice useQuasar is followed by a comma)
        // import { QTable, useQuasar, } from 'quasar'
        if (importName === '') {
          return ''
        }

        const importAs = data[ 1 ] !== void 0
          ? data[ 1 ].trim()
          : importName;

        importMap[ importName ] = importAs;
        return `import ${ importAs } from '${ importTransformation(importName) }';`
      })
      .join('')
  )
}

/**
 * Transforms JS code where importing from 'quasar' package
 * Example:
 *       import { QBtn } from 'quasar'
 *    -> add to importMap & importList & remove original import statement
 *    (removing original is required so that the end file will have
 *     only one import from Quasar statement)
 */
function removeQuasarImports (code, importMap, importSet, reverseMap) {
  return code.replace(
    importQuasarRegex,
    (_, match) => {
      match.split(',').forEach(identifier => {
        const data = identifier.split(' as ');
        const importName = data[ 0 ].trim();

        // might be an empty entry like below
        // (notice useQuasar is followed by a comma)
        // import { QTable, useQuasar, } from 'quasar'
        if (importName !== '') {
          const importAs = data[ 1 ] !== void 0
            ? data[ 1 ].trim()
            : importName;

          importSet.add(importName + (importAs !== importName ? ` as ${ importAs }` : ''));
          importMap[ importName ] = importAs;
          reverseMap[ importName.replace(/-/g, '_') ] = importAs;
        }
      });

      // we registered the original import and we
      // remove this one to avoid duplicate imports from Quasar
      return ''
    }
  )
}

let transformState;
function useTransformState () {
  if (transformState !== void 0) {
    return transformState
  }

  const autoImportData = JSON.parse(
    node_fs.readFileSync(node_path.join(quasarPath, 'dist/transforms/auto-import.json'), 'utf-8')
  );

  const compRegex = {
    kebab: new RegExp(`_resolveComponent\\("${ autoImportData.regex.kebabComponents }"\\)`, 'g'),
    pascal: new RegExp(`_resolveComponent\\("${ autoImportData.regex.pascalComponents }"\\)`, 'g'),
    combined: new RegExp(`_resolveComponent\\("${ autoImportData.regex.components }"\\)`, 'g')
  };

  const dirRegex = new RegExp(`_resolveDirective\\("${ autoImportData.regex.directives.replace(/v-/g, '') }"\\)`, 'g');

  transformState = {
    autoImportData,
    compRegex,
    dirRegex
  };
  return transformState
}

const lengthSortFn = (a, b) => b.length - a.length;

function vueTransform (content, autoImportComponentCase, useTreeshaking) {
  const { autoImportData, compRegex, dirRegex } = useTransformState();

  const importSet = new Set();
  const importMap = {};

  const compList = [];
  const dirList = [];

  const reverseMap = {};
  const jsImportTransformed = useTreeshaking === true
    ? mapQuasarImports(content, importMap)
    : removeQuasarImports(content, importMap, importSet, reverseMap);

  let code = jsImportTransformed
    .replace(compRegex[ autoImportComponentCase ], (_, match) => {
      const name = autoImportData.importName[ match ];
      const reverseName = match.replace(/-/g, '_');

      if (importMap[ name ] === void 0) {
        importSet.add(name);
        reverseMap[ reverseName ] = name;
      }
      else {
        reverseMap[ reverseName ] = importMap[ name ];
      }

      compList.push(reverseName);
      return ''
    })
    .replace(dirRegex, (_, match) => {
      const name = autoImportData.importName[ 'v-' + match ];
      const reverseName = match.replace(/-/g, '_');

      if (importMap[ name ] === void 0) {
        importSet.add(name);
        reverseMap[ reverseName ] = name;
      }
      else {
        reverseMap[ reverseName ] = importMap[ name ];
      }

      dirList.push(reverseName);
      return ''
    });

  if (compList.length !== 0) {
    const list = compList.sort(lengthSortFn).join('|');
    code = code
      .replace(new RegExp(`const _component_(${ list }) = `, 'g'), '')
      .replace(new RegExp(`_component_(${ list })`, 'g'), (_, match) => reverseMap[ match ]);
  }

  if (dirList.length !== 0) {
    const list = dirList.sort(lengthSortFn).join('|');
    code = code
      .replace(new RegExp(`const _directive_(${ list }) = `, 'g'), '')
      .replace(new RegExp(`_directive_(${ list })`, 'g'), (_, match) => reverseMap[ match ]);
  }

  if (importSet.size === 0) {
    return code
  }

  const importList = [ ...importSet ];
  const codePrefix = useTreeshaking === true
    ? importList.map(name => `import ${ name } from '${ importTransformation(name) }'`).join(';')
    : `import {${ importList.join(',') }} from 'quasar'`;

  return codePrefix + ';' + code
}

function createScssTransform (fileExtension, sassVariables) {
  const sassImportCode = [ '@import \'quasar/src/css/variables.sass\'', '' ];

  if (typeof sassVariables === 'string') {
    sassImportCode.unshift(`@import '${ sassVariables }'`);
  }

  const prefix = fileExtension === 'sass'
    ? sassImportCode.join('\n')
    : sassImportCode.join(';\n');

  return content => {
    const useIndex = Math.max(
      content.lastIndexOf('@use '),
      content.lastIndexOf('@forward ')
    );

    if (useIndex === -1) {
      return prefix + content
    }

    const newLineIndex = content.indexOf('\n', useIndex);

    if (newLineIndex !== -1) {
      const index = newLineIndex + 1;
      return content.substring(0, index) + prefix + content.substring(index)
    }

    return content + '\n' + prefix
  }
}

/**
 * @typedef {{
 *  extensionsList: string[];
 *  filenameRegex: RegExp;
 * }} ExtMatcher
 *
 * @typedef {{
 *  template: (extMatcher: ExtMatcher) => boolean;
 *  script: (extMatcher: ExtMatcher) => boolean;
 *  style: (extMatcher: ExtMatcher) => boolean;
 * }} ViteQueryIs
 */

/**
 * @see https://github.com/vitejs/vite/blob/364aae13f0826169e8b1c5db41ac6b5bb2756958/packages/plugin-vue/src/utils/query.ts - source of inspiration
 * @example
 * '/absolute/path/src/App.vue' // Can contain combined template&script -> template: true, script: true, style: false
 * '/absolute/path/src/App.vue?vue&type=script&lang.js' // Only contains script -> template: false, script: true, style: false
 * '/absolute/path/src/App.vue?vue&type=style&lang.sass' // Only contains style -> template: false, script: false, style: true
 * '/absolute/path/src/script.js' // Only contains script -> template: false, script: true, style: false
 * '/absolute/path/src/index.scss' // Only contains style -> template: false, script: false, style: true
 *
 * @param {string} id
 * @returns {{ filename: string; query: { [key: string]: string; }; is: ViteQueryIs }}
 */
function parseViteRequest (id) {
  const [ filename, rawQuery ] = id.split('?', 2);
  const query = Object.fromEntries(new URLSearchParams(rawQuery));

  if (query.raw !== void 0) {
    return {
      // if it's a ?raw request, then don't touch it at all
      template: () => false,
      script: () => false,
      style: () => false
    }
  }

  return query.vue !== void 0 // is vue query?
    ? {
        template: () => (
          query.type === void 0
          || query.type === 'template'
          // On prod, TS code turns into a separate 'script' request.
          // See: https://github.com/vitejs/vite/pull/7909
          || (query.type === 'script' && (query[ 'lang.ts' ] !== void 0 || query[ 'lang.tsx' ] !== void 0))
        ),

        script: matcher => (
          (query.type === void 0 || query.type === 'script')
          && matcher.extensionsList.some(ext => query[ `lang.${ ext }` ] !== void 0) === true
        ),

        style: matcher => (
          query.type === 'style'
          && matcher.extensionsList.some(ext => query[ `lang.${ ext }` ] !== void 0) === true
        )
      }
    : {
        template: matcher => matcher.filenameRegex.test(filename),
        script: matcher => matcher.filenameRegex.test(filename),
        style: matcher => matcher.filenameRegex.test(filename)
      }
}

function createExtMatcher (extensionsList) {
  return {
    extensionsList,
    filenameRegex: new RegExp(`\\.(${ extensionsList.join('|') })$`)
  }
}

const defaultOptions = {
  runMode: 'web-client',
  sassVariables: true,
  devTreeshaking: false,
  autoImportComponentCase: 'kebab',
  autoImportVueExtensions: [ 'vue' ],
  autoImportScriptExtensions: [ 'js', 'jsx', 'ts', 'tsx' ]
};
const defaultOptionsKeys = Object.keys(defaultOptions);

function parsePluginOptions (userOpts = {}) {
  const opts = { ...userOpts };

  for (const key of defaultOptionsKeys) {
    if (opts[ key ] === void 0) {
      opts[ key ] = defaultOptions[ key ];
    }
  }

  return opts
}

function getConfigPlugin (opts) {
  return {
    name: 'vite:quasar:vite-conf',

    configResolved (viteConf) {
      const vueCfg = viteConf.plugins.find(entry => entry.name === 'vite:vue');

      if (vueCfg === void 0) {
        console.error('\n\n[Quasar] Error: In your Vite config file, please add the Quasar plugin ** after ** the Vue one\n\n');
        process.exit(1);
      }
    },

    config (viteConf, { mode }) {
      return getViteConfig(opts.runMode, mode, viteConf)
    }
  }
}

const scssMatcher = createExtMatcher([ 'scss', 'module.scss' ]);
const sassMatcher = createExtMatcher([ 'sass', 'module.sass' ]);

function getScssTransformsPlugin (opts) {
  const sassVariables = typeof opts.sassVariables === 'string'
    ? vite.normalizePath(opts.sassVariables)
    : opts.sassVariables;

  const scssTransform = createScssTransform('scss', sassVariables);
  const sassTransform = createScssTransform('sass', sassVariables);

  return {
    name: 'vite:quasar:scss',

    enforce: 'pre',

    transform (src, id) {
      const is = parseViteRequest(id);

      if (is.style(scssMatcher) === true) {
        return {
          code: scssTransform(src),
          map: null
        }
      }

      if (is.style(sassMatcher) === true) {
        return {
          code: sassTransform(src),
          map: null
        }
      }

      return null
    }
  }
}

function getScriptTransformsPlugin (opts) {
  let useTreeshaking = true;

  const vueMatcher = createExtMatcher(opts.autoImportVueExtensions);
  const scriptMatcher = createExtMatcher(opts.autoImportScriptExtensions);

  return {
    name: 'vite:quasar:script',

    configResolved (resolvedConfig) {
      if (opts.devTreeshaking === false && resolvedConfig.mode !== 'production') {
        useTreeshaking = false;
      }
      else {
        loadQuasarImportMap();
      }
    },

    transform (src, id) {
      const is = parseViteRequest(id);

      if (is.template(vueMatcher) === true) {
        return {
          code: vueTransform(src, opts.autoImportComponentCase, useTreeshaking),
          map: null // provide source map if available
        }
      }

      if (useTreeshaking === true && is.script(scriptMatcher) === true) {
        return {
          code: mapQuasarImports(src),
          map: null // provide source map if available
        }
      }

      return null
    }
  }
}

function plugin (userOpts) {
  const opts = parsePluginOptions(userOpts);

  const plugins = [
    getConfigPlugin(opts)
  ];

  if (opts.sassVariables) {
    plugins.push(
      getScssTransformsPlugin(opts)
    );
  }

  if (opts.runMode !== 'ssr-server') {
    plugins.push(
      getScriptTransformsPlugin(opts)
    );
  }

  return plugins
}

const transformAssetUrls = JSON.parse(
  node_fs.readFileSync(node_path.join(quasarPath, 'dist/transforms/loader-asset-urls.json'), 'utf-8')
);

exports.quasar = plugin;
exports.transformAssetUrls = transformAssetUrls;
