'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var path = require('path');
var compiler = require('@vanilla-extract/compiler');
var integration = require('@vanilla-extract/integration');

function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }

var path__default = /*#__PURE__*/_interopDefault(path);

const PLUGIN_NAME = 'vite-plugin-vanilla-extract';
const virtualExtCss = '.vanilla.css';
const isVirtualId = id => id.endsWith(virtualExtCss);
const fileIdToVirtualId = id => `${id}${virtualExtCss}`;
const virtualIdToFileId = virtualId => virtualId.slice(0, -virtualExtCss.length);
const isPluginObject = plugin => typeof plugin === 'object' && plugin !== null && 'name' in plugin;
// Plugins that we know are compatible with the `vite-node` compiler
// and don't need to be filtered out.
const COMPATIBLE_PLUGINS = ['vite-tsconfig-paths'];
const defaultPluginFilter = ({
  name
}) => COMPATIBLE_PLUGINS.includes(name);
const withUserPluginFilter = ({
  mode,
  pluginFilter
}) => plugin => pluginFilter({
  name: plugin.name,
  mode
});
function vanillaExtractPlugin({
  identifiers,
  unstable_pluginFilter: pluginFilter = defaultPluginFilter,
  unstable_mode = 'emitCss'
} = {}) {
  let config;
  let configEnv;
  let server;
  let packageName;
  let compiler$1;
  let isBuild;
  const vitePromise = import('vite');
  const getIdentOption = () => identifiers ?? (config.mode === 'production' ? 'short' : 'debug');
  const getAbsoluteId = filePath => {
    let resolvedId = filePath;
    if (filePath.startsWith(config.root) ||
    // In monorepos the absolute path will be outside of config.root, so we check that they have the same root on the file system
    // Paths from vite are always normalized, so we have to use the posix path separator
    path__default["default"].isAbsolute(filePath) && filePath.split(path__default["default"].posix.sep)[1] === config.root.split(path__default["default"].posix.sep)[1]) {
      resolvedId = filePath;
    } else {
      // In SSR mode we can have paths like /app/styles.css.ts
      resolvedId = path__default["default"].join(config.root, filePath);
    }
    return integration.normalizePath(resolvedId);
  };
  function invalidateModule(absoluteId) {
    if (!server) return;
    const {
      moduleGraph
    } = server;
    const modules = moduleGraph.getModulesByFile(absoluteId);
    if (modules) {
      for (const module of modules) {
        moduleGraph.invalidateModule(module);

        // Vite uses this timestamp to add `?t=` query string automatically for HMR.
        module.lastHMRTimestamp = module.lastInvalidationTimestamp || Date.now();
      }
    }
  }
  return {
    name: PLUGIN_NAME,
    configureServer(_server) {
      server = _server;
    },
    config(_userConfig, _configEnv) {
      configEnv = _configEnv;
      return {
        ssr: {
          external: ['@vanilla-extract/css', '@vanilla-extract/css/fileScope', '@vanilla-extract/css/adapter']
        }
      };
    },
    async configResolved(_resolvedConfig) {
      config = _resolvedConfig;
      isBuild = config.command === 'build' && !config.build.watch;
      packageName = integration.getPackageInfo(config.root).name;
    },
    async buildStart() {
      // Ensure we re-use the compiler instance between builds, e.g. in watch mode
      if (unstable_mode !== 'transform' && !compiler$1) {
        var _configForViteCompile;
        const {
          loadConfigFromFile
        } = await vitePromise;
        let configForViteCompiler;

        // The user has a vite config file
        if (config.configFile) {
          const configFile = await loadConfigFromFile({
            command: config.command,
            mode: config.mode,
            isSsrBuild: configEnv.isSsrBuild
          }, config.configFile);
          configForViteCompiler = configFile === null || configFile === void 0 ? void 0 : configFile.config;
        }
        // The user is using a vite-based framework that has a custom config file
        else {
          configForViteCompiler = config.inlineConfig;
        }
        const viteConfig = {
          ...configForViteCompiler,
          plugins: (_configForViteCompile = configForViteCompiler) === null || _configForViteCompile === void 0 || (_configForViteCompile = _configForViteCompile.plugins) === null || _configForViteCompile === void 0 ? void 0 : _configForViteCompile.flat().filter(isPluginObject).filter(withUserPluginFilter({
            mode: config.mode,
            pluginFilter
          }))
        };
        compiler$1 = compiler.createCompiler({
          root: config.root,
          identifiers: getIdentOption(),
          cssImportSpecifier: fileIdToVirtualId,
          viteConfig,
          enableFileWatcher: !isBuild
        });
      }
    },
    buildEnd() {
      // When using the rollup watcher, we don't want to close the compiler after every build.
      // Instead, we close it when the watcher is closed via the closeWatcher hook.
      if (!config.build.watch) {
        var _compiler;
        (_compiler = compiler$1) === null || _compiler === void 0 || _compiler.close();
      }
    },
    closeWatcher() {
      var _compiler2;
      return (_compiler2 = compiler$1) === null || _compiler2 === void 0 ? void 0 : _compiler2.close();
    },
    async transform(code, id) {
      const [validId] = id.split('?');
      if (!integration.cssFileFilter.test(validId)) {
        return null;
      }
      const identOption = getIdentOption();
      if (unstable_mode === 'transform') {
        return integration.transform({
          source: code,
          filePath: integration.normalizePath(validId),
          rootPath: config.root,
          packageName,
          identOption
        });
      }
      if (compiler$1) {
        const absoluteId = getAbsoluteId(validId);
        const {
          source,
          watchFiles
        } = await compiler$1.processVanillaFile(absoluteId, {
          outputCss: true
        });
        const result = {
          code: source,
          map: {
            mappings: ''
          }
        };

        // We don't need to watch files in build mode
        if (isBuild) {
          return result;
        }
        for (const file of watchFiles) {
          if (!file.includes('node_modules') && integration.normalizePath(file) !== absoluteId) {
            this.addWatchFile(file);
          }

          // We have to invalidate the virtual module & deps, not the real one we just transformed
          // The deps have to be invalidated in case one of them changing was the trigger causing
          // the current transformation
          if (integration.cssFileFilter.test(file)) {
            invalidateModule(fileIdToVirtualId(file));
          }
        }
        return result;
      }
    },
    resolveId(source) {
      var _compiler3;
      const [validId, query] = source.split('?');
      if (!isVirtualId(validId)) return;
      const absoluteId = getAbsoluteId(validId);
      if ( // We should always have CSS for a file here.
      // The only valid scenario for a missing one is if someone had written
      // a file in their app using the .vanilla.js/.vanilla.css extension
      (_compiler3 = compiler$1) !== null && _compiler3 !== void 0 && _compiler3.getCssForFile(virtualIdToFileId(absoluteId))) {
        // Keep the original query string for HMR.
        return absoluteId + (query ? `?${query}` : '');
      }
    },
    load(id) {
      const [validId] = id.split('?');
      if (!isVirtualId(validId) || !compiler$1) return;
      const absoluteId = getAbsoluteId(validId);
      const {
        css
      } = compiler$1.getCssForFile(virtualIdToFileId(absoluteId));
      return css;
    }
  };
}

exports.vanillaExtractPlugin = vanillaExtractPlugin;
