require("core-js/modules/es.array.iterator");

require("core-js/modules/es.string.replace");

var _Object$defineProperty = require("@babel/runtime-corejs3/core-js-stable/object/define-property");

var _Object$defineProperties = require("@babel/runtime-corejs3/core-js-stable/object/define-properties");

var _Object$getOwnPropertyDescriptors = require("@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptors");

var _forEachInstanceProperty = require("@babel/runtime-corejs3/core-js-stable/instance/for-each");

var _Object$getOwnPropertyDescriptor = require("@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptor");

var _filterInstanceProperty = require("@babel/runtime-corejs3/core-js-stable/instance/filter");

var _Object$getOwnPropertySymbols = require("@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols");

var _Object$keys = require("@babel/runtime-corejs3/core-js-stable/object/keys");

var _startsWithInstanceProperty = require("@babel/runtime-corejs3/core-js-stable/instance/starts-with");

var _spliceInstanceProperty = require("@babel/runtime-corejs3/core-js-stable/instance/splice");

var _lastIndexOfInstanceProperty = require("@babel/runtime-corejs3/core-js-stable/instance/last-index-of");

var _indexOfInstanceProperty = require("@babel/runtime-corejs3/core-js-stable/instance/index-of");

var _defineProperty = require("@babel/runtime-corejs3/helpers/defineProperty");

var _bindInstanceProperty = require("@babel/runtime-corejs3/core-js-stable/instance/bind");

function ownKeys(object, enumerableOnly) { var keys = _Object$keys(object); if (_Object$getOwnPropertySymbols) { var symbols = _Object$getOwnPropertySymbols(object); if (enumerableOnly) symbols = _filterInstanceProperty(symbols).call(symbols, function (sym) { return _Object$getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { var _context8; _forEachInstanceProperty(_context8 = ownKeys(source, true)).call(_context8, function (key) { _defineProperty(target, key, source[key]); }); } else if (_Object$getOwnPropertyDescriptors) { _Object$defineProperties(target, _Object$getOwnPropertyDescriptors(source)); } else { var _context9; _forEachInstanceProperty(_context9 = ownKeys(source)).call(_context9, function (key) { _Object$defineProperty(target, key, _Object$getOwnPropertyDescriptor(source, key)); }); } } return target; }

/* eslint-disable class-methods-use-this */
const fs = require('fs');

const path = require('path');

const less = require('less');

const chalk = require('chalk');

const loaderUtils = require('loader-utils');

const pify = require('pify');

const max = require('lodash/max');

const getThemePath = require('../utils/cosmic-less-theme');

const stringifyLoader = require.resolve('less-loader/dist/stringifyLoader');

const trailingSlash = /[/\\]$/;
const isLessCompatible = /\.(le|c)ss$/; // Less automatically adds a .less file extension if no extension was given.
// This is problematic if there is a module request like @import "~some-module";
// because in this case Less will call our file manager with `~some-module.less`.
// Since dots in module names are highly discouraged, we can safely assume that
// this is an error and we need to remove the .less extension again.
// However, we must not match something like @import "~some-module/file.less";

const matchMalformedModuleFilename = /(~[^/\\]+)\.less$/; // This somewhat changed in Less 3.x. Now the file name comes without the
// automatically added extension whereas the extension is passed in as `options.ext`.
// So, if the file name matches this regexp, we simply ignore the proposed extension.

const isModuleName = /^~[^/\\]+$/; // Plugin is based of less-loader https://github.com/webpack-contrib/less-loader/blob/master/src/createWebpackLessPlugin.js

module.exports = function createCosmicLessResolver(options) {
  var _context, _context2, _context3;

  const loaderContext = options.loaderContext;
  const resolve = pify(_bindInstanceProperty(_context = loaderContext.resolve).call(_context, loaderContext));
  const loadModule = pify(_bindInstanceProperty(_context2 = loaderContext.loadModule).call(_context2, loaderContext));
  const readFile = pify(_bindInstanceProperty(_context3 = loaderContext.fs.readFile).call(_context3, loaderContext.fs));

  class CosmicLessResolver extends less.FileManager {
    constructor(options) {
      super();
      this.options = _objectSpread({
        app: process.cwd()
      }, options);
    }

    supports() {
      // Our WebpackFileManager handles all the files
      return true;
    } // Sync resolving is used at least by the `data-uri` function.
    // This file manager doesn't know how to do it, so let's delegate it
    // to the default file manager of Less.
    // We could probably use loaderContext.resolveSync, but it's deprecated,
    // see https://webpack.js.org/api/loaders/#this-resolvesync


    supportsSync() {
      return false;
    }

    loadFile(filename, currentDirectory, options) {
      let useBase = false; // Way of importing to theme file and override instead of copying all required methods to the new file

      if (_indexOfInstanceProperty(filename).call(filename, 'base:') === 0) {
        useBase = true;
        filename = filename.replace('base:', '');
      }

      let url;

      if (less.version[0] >= 3) {
        if (options.ext && !isModuleName.test(filename)) {
          url = this.tryAppendExtension(filename, options.ext);
        } else {
          url = filename;
        }
      } else {
        url = filename.replace(matchMalformedModuleFilename, '$1');
      }

      const moduleRequest = loaderUtils.urlToRequest(url, url.charAt(0) === '/' ? '' : null); // console.log('moduleRequest', moduleRequest)
      // Less is giving us trailing slashes, but the context should have no trailing slash

      let context = currentDirectory.replace(trailingSlash, '');
      let resolvedFilename;
      const themePath = getThemePath(this.options.theme);
      const scope = this.options.scope || ''; // We want to resolve from the base directory not the theme directory

      if (this.isInsideTheme(currentDirectory, themePath)) {
        context = this.resolvePath(context);
      }

      return resolve(context, moduleRequest).then(f => {
        resolvedFilename = f;

        if (!useBase) {
          const resolvedCustomFilename = this.resolveCustomPath(resolvedFilename, themePath, scope);

          if (fs.existsSync(resolvedCustomFilename)) {
            this.console(chalk.blue('REPLACED', resolvedCustomFilename));
            return resolvedCustomFilename;
          } else {
            this.console('FILE', resolvedFilename);
          }
        } else {
          this.console('USE BASE', resolvedFilename);
        }

        return resolvedFilename;
      }).then(f => {
        resolvedFilename = f;
        loaderContext.addDependency(resolvedFilename);

        if (isLessCompatible.test(resolvedFilename)) {
          return readFile(resolvedFilename).then(contents => contents.toString('utf8'));
        }

        return loadModule([stringifyLoader, resolvedFilename].join('!')).then(JSON.parse);
      }).then(contents => ({
        contents,
        filename: resolvedFilename
      })).catch(e => {
        console.log(chalk.red('-------------- ERROR (custom-less-resolver) -----------------'));
        console.log('FILENAME', filename);
        console.log('CURRENT DIRECTORY', context);
        console.log('ERROR:', e);
      });
    }
    /**
     * Transforms path to file so it resolves to theme file instead of base file
     * @param resolvedFilename - Absolute path to file
     * @returns {string} - Transformed path to file
     */


    resolveCustomPath(resolvedFilename, theme, scope) {
      var _context4;

      // We dont want to override direct import of theme
      if (_indexOfInstanceProperty(_context4 = path.normalize(resolvedFilename)).call(_context4, path.normalize(theme)) > -1) {
        return resolvedFilename;
      }

      const originalPathArr = resolvedFilename.split(path.sep);

      const lastIndexOfPackages = _lastIndexOfInstanceProperty(originalPathArr).call(originalPathArr, 'packages');

      const lastIndexOfModules = _lastIndexOfInstanceProperty(originalPathArr).call(originalPathArr, 'node_modules');

      const pathRest = _spliceInstanceProperty(originalPathArr).call(originalPathArr, max([lastIndexOfPackages, lastIndexOfModules]) + 1);

      if (Boolean(scope) && scope.length) {
        //If scope is given and package is not scoped it might be linked,
        //so we need to change the url to a scoped url
        //because the theme directory expects a scoped path
        if (pathRest[0] !== scope) {
          var _context5;

          const linkedScopeName = scope.replace('@', '') + '-';
          const scopePath = scope + '/';

          if (_startsWithInstanceProperty(_context5 = pathRest[0]).call(_context5, linkedScopeName)) {
            pathRest[0] = pathRest[0].replace(linkedScopeName, scopePath);
          }
        }
      }

      const customPath = path.join(theme, pathRest.join(path.sep)).replace(path.sep + 'src' + path.sep, path.sep);

      if (this.options.debug) {
        console.log('customPath', customPath);
      }

      return customPath;
    }
    /**
     * Transforms path to file so it resolves to base file instead of theme file
     * @param filename - Absolute path to file
     * @returns {string} - Transformed path to file
     */


    resolvePath(filename) {
      const originalPathArr = filename.split(path.sep);

      const lastIndexOfPackages = _lastIndexOfInstanceProperty(originalPathArr).call(originalPathArr, 'packages');

      const lastIndexOfModules = _lastIndexOfInstanceProperty(originalPathArr).call(originalPathArr, 'node_modules'); //console.log('lastIndex', originalPathArr.splice(max([lastIndexOfPackages, lastIndexOfModules]) + 1))


      const pathRest = _spliceInstanceProperty(originalPathArr).call(originalPathArr, max([lastIndexOfPackages, lastIndexOfModules]) + 1);

      _spliceInstanceProperty(pathRest).call(pathRest, 0, 1); // remove theme


      _spliceInstanceProperty(pathRest).call(pathRest, 1, 0, 'src'); // add src folder


      if (this.options.debug) {
        console.log('resolve', path.join(this.options.app, 'node_modules', pathRest.join(path.sep)));
      }

      return path.join(this.options.app, 'node_modules', pathRest.join(path.sep));
    }
    /**
     * Checks if path resolves to a file inside theme
     * @param filename - Absolute path to file
     * @returns {boolean}
     */


    isInsideTheme(filename, theme) {
      var _context6, _context7;

      return _indexOfInstanceProperty(_context6 = filename.toLowerCase()).call(_context6, theme.toLowerCase()) > -1 && _indexOfInstanceProperty(_context7 = filename.toLowerCase()).call(_context7, path.join(theme.toLowerCase(), 'styles')) === -1; // We don't want to mess with the /styles folder
    }
    /**
     * Prints to console if debug is on
     * @param args
     */


    console(...args) {
      if (this.options.debug) {
        console.log(...args);
      }
    }

  }

  return {
    install(lessInstance, pluginManager) {
      pluginManager.addFileManager(new CosmicLessResolver(options));
    },

    minVersion: [2, 1, 1]
  };
};