/*!
 * @nuxt/core v2.15.8 (c) 2016-2021
 * Released under the MIT License
 * Repository: https://github.com/nuxt/nuxt.js
 * Website: https://nuxtjs.org
*/
'use strict';

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

const path = require('path');
const fs = require('fs');
const hash = require('hash-sum');
const consola = require('consola');
const utils = require('@nuxt/utils');
const lodash = require('lodash');
const Hookable = require('hable');
const config = require('@nuxt/config');
const server = require('@nuxt/server');
const fs$1 = require('fs-extra');

function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

const path__default = /*#__PURE__*/_interopDefaultLegacy(path);
const fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
const hash__default = /*#__PURE__*/_interopDefaultLegacy(hash);
const consola__default = /*#__PURE__*/_interopDefaultLegacy(consola);
const Hookable__default = /*#__PURE__*/_interopDefaultLegacy(Hookable);
const fs__default$1 = /*#__PURE__*/_interopDefaultLegacy(fs$1);

class ModuleContainer {
  constructor (nuxt) {
    this.nuxt = nuxt;
    this.options = nuxt.options;
    this.requiredModules = {};

    // Self bind to allow destructre from container
    for (const method of Object.getOwnPropertyNames(ModuleContainer.prototype)) {
      if (typeof this[method] === 'function') {
        this[method] = this[method].bind(this);
      }
    }
  }

  async ready () {
    // Call before hook
    await this.nuxt.callHook('modules:before', this, this.options.modules);

    if (this.options.buildModules && !this.options._start) {
      // Load every devModule in sequence
      await utils.sequence(this.options.buildModules, this.addModule);
    }

    // Load every module in sequence
    await utils.sequence(this.options.modules, this.addModule);

    // Load ah-hoc modules last
    await utils.sequence(this.options._modules, this.addModule);

    // Call done hook
    await this.nuxt.callHook('modules:done', this);
  }

  addVendor () {
    consola__default['default'].warn('addVendor has been deprecated due to webpack4 optimization');
  }

  addTemplate (template) {
    if (!template) {
      throw new Error('Invalid template: ' + JSON.stringify(template))
    }

    // Validate & parse source
    const src = template.src || template;
    const srcPath = path__default['default'].parse(src);

    if (typeof src !== 'string' || !fs__default['default'].existsSync(src)) {
      throw new Error('Template src not found: ' + src)
    }

    // Mostly for DX, some people prefers `filename` vs `fileName`
    const fileName = template.fileName || template.filename;
    // Generate unique and human readable dst filename if not provided
    const dst = fileName || `${path__default['default'].basename(srcPath.dir)}.${srcPath.name}.${hash__default['default'](src)}${srcPath.ext}`;
    // Add to templates list
    const templateObj = {
      src,
      dst,
      options: template.options
    };

    this.options.build.templates.push(templateObj);

    return templateObj
  }

  addPlugin (template) {
    const { dst } = this.addTemplate(template);

    // Add to nuxt plugins
    this.options.plugins.unshift({
      src: path__default['default'].join(this.options.buildDir, dst),
      // TODO: remove deprecated option in Nuxt 3
      ssr: template.ssr,
      mode: template.mode
    });
  }

  addLayout (template, name) {
    const { dst, src } = this.addTemplate(template);
    const layoutName = name || path__default['default'].parse(src).name;
    const layout = this.options.layouts[layoutName];

    if (layout) {
      consola__default['default'].warn(`Duplicate layout registration, "${layoutName}" has been registered as "${layout}"`);
    }

    // Add to nuxt layouts
    this.options.layouts[layoutName] = `./${dst}`;

    // If error layout, set ErrorPage
    if (name === 'error') {
      this.addErrorLayout(dst);
    }
  }

  addErrorLayout (dst) {
    const relativeBuildDir = path__default['default'].relative(this.options.rootDir, this.options.buildDir);
    this.options.ErrorPage = `~/${relativeBuildDir}/${dst}`;
  }

  addServerMiddleware (middleware) {
    this.options.serverMiddleware.push(middleware);
  }

  extendBuild (fn) {
    this.options.build.extend = utils.chainFn(this.options.build.extend, fn);
  }

  extendRoutes (fn) {
    this.options.router.extendRoutes = utils.chainFn(
      this.options.router.extendRoutes,
      fn
    );
  }

  requireModule (moduleOpts, { paths } = {}) {
    return this.addModule(moduleOpts, undefined, { paths })
  }

  async addModule (moduleOpts, arg2, arg3) {
    // Arg2 was previously used for requireOnce which is ignored now
    const { paths } = { ...arg2, ...arg3 };
    let src;
    let options;
    let handler;

    // Type 1: String or Function
    if (typeof moduleOpts === 'string' || typeof moduleOpts === 'function') {
      src = moduleOpts;
    } else if (Array.isArray(moduleOpts)) {
      // Type 2: Babel style array
      [src, options] = moduleOpts;
    } else if (typeof moduleOpts === 'object') {
      // Type 3: Pure object
      ({ src, options, handler } = moduleOpts);
    }

    // Define handler if src is a function
    if (typeof src === 'function') {
      handler = src;
    }

    // Prevent adding buildModules-listed entries in production
    if (this.options.buildModules.includes(handler) && this.options._start) {
      return
    }

    // Resolve handler
    if (!handler) {
      try {
        handler = this.nuxt.resolver.requireModule(src, { paths });
        // pnp support
        try { (global.__NUXT_PATHS__ || []).push(this.nuxt.resolver.resolvePath(src, { paths })); } catch (_err) {}
      } catch (error) {
        if (error.code !== 'MODULE_NOT_FOUND') {
          throw error
        }

        // Hint only if entrypoint is not found and src is not local alias or path
        if (error.message.includes(src) && !/^[~.]|^@\//.test(src)) {
          let message = 'Module `{name}` not found.';

          if (this.options.buildModules.includes(src)) {
            message += ' Please ensure `{name}` is in `devDependencies` and installed. HINT: During build step, for npm/yarn, `NODE_ENV=production` or `--production` should NOT be used.'.replace('{name}', src);
          } else if (this.options.modules.includes(src)) {
            message += ' Please ensure `{name}` is in `dependencies` and installed.';
          }

          message = message.replace(/{name}/g, src);

          consola__default['default'].warn(message);
        }

        if (this.options._cli) {
          throw error
        } else {
          // TODO: Remove in next major version
          consola__default['default'].warn('Silently ignoring module as programatic usage detected.');
          return
        }
      }
    }

    // Validate handler
    if (typeof handler !== 'function') {
      throw new TypeError('Module should export a function: ' + src)
    }

    // Ensure module is required once
    const metaKey = handler.meta && handler.meta.name;
    const key = metaKey || src;
    if (typeof key === 'string') {
      if (this.requiredModules[key]) {
        if (!metaKey) {
          // TODO: Skip with nuxt3
          consola__default['default'].warn('Modules should be only specified once:', key);
        } else {
          return
        }
      }
      this.requiredModules[key] = { src, options, handler };
    }

    // Default module options to empty object
    if (options === undefined) {
      options = {};
    }
    const result = await handler.call(this, options);
    return result
  }
}

var version = "2.15.8";

class Resolver {
  constructor (nuxt) {
    this.nuxt = nuxt;
    this.options = this.nuxt.options;

    // Binds
    this.resolvePath = this.resolvePath.bind(this);
    this.resolveAlias = this.resolveAlias.bind(this);
    this.resolveModule = this.resolveModule.bind(this);
    this.requireModule = this.requireModule.bind(this);

    this._createRequire = this.options.createRequire || utils.createRequire;
    this._require = this._createRequire(__filename);
  }

  resolveModule (path, { paths } = {}) {
    try {
      return this._require.resolve(path, {
        paths: [].concat(global.__NUXT_PREPATHS__ || [], paths || [], this.options.modulesDir, global.__NUXT_PATHS__ || [], process.cwd())
      })
    } catch (error) {
      if (error.code !== 'MODULE_NOT_FOUND') {
        throw error
      }
    }
  }

  resolveAlias (path$1) {
    if (utils.startsWithRootAlias(path$1)) {
      return path.join(this.options.rootDir, path$1.substr(2))
    }

    if (utils.startsWithSrcAlias(path$1)) {
      return path.join(this.options.srcDir, path$1.substr(1))
    }

    return path.resolve(this.options.srcDir, path$1)
  }

  resolvePath (path$1, { alias, isAlias = alias, module, isModule = module, isStyle, paths } = {}) {
    // TODO: Remove in Nuxt 3
    if (alias) {
      consola__default['default'].warn('Using alias is deprecated and will be removed in Nuxt 3. Use `isAlias` instead.');
    }
    if (module) {
      consola__default['default'].warn('Using module is deprecated and will be removed in Nuxt 3. Use `isModule` instead.');
    }

    // Fast return in case of path exists
    if (fs__default$1['default'].existsSync(path$1)) {
      return path$1
    }

    let resolvedPath;

    // Try to resolve it as a regular module
    if (isModule !== false) {
      resolvedPath = this.resolveModule(path$1, { paths });
    }

    // Try to resolve alias
    if (!resolvedPath && isAlias !== false) {
      resolvedPath = this.resolveAlias(path$1);
    }

    // Use path for resolvedPath
    if (!resolvedPath) {
      resolvedPath = path$1;
    }

    let isDirectory;

    // Check if resolvedPath exits and is not a directory
    if (fs__default$1['default'].existsSync(resolvedPath)) {
      isDirectory = fs__default$1['default'].lstatSync(resolvedPath).isDirectory();

      if (!isDirectory) {
        return resolvedPath
      }
    }

    const extensions = isStyle ? this.options.styleExtensions : this.options.extensions;

    // Check if any resolvedPath.[ext] or resolvedPath/index.[ext] exists
    for (const ext of extensions) {
      if (!isDirectory && fs__default$1['default'].existsSync(resolvedPath + '.' + ext)) {
        return resolvedPath + '.' + ext
      }

      const resolvedPathwithIndex = path.join(resolvedPath, 'index.' + ext);
      if (isDirectory && fs__default$1['default'].existsSync(resolvedPathwithIndex)) {
        return resolvedPathwithIndex
      }
    }

    // If there's no index.[ext] we just return the directory path
    if (isDirectory) {
      return resolvedPath
    }

    // Give up
    throw new Error(`Cannot resolve "${path$1}" from "${resolvedPath}"`)
  }

  requireModule (path, { alias, isAlias = alias, intropDefault, interopDefault = intropDefault, paths } = {}) {
    let resolvedPath = path;
    let requiredModule;

    // TODO: Remove in Nuxt 3
    if (intropDefault) {
      consola__default['default'].warn('Using intropDefault is deprecated and will be removed in Nuxt 3. Use `interopDefault` instead.');
    }
    if (alias) {
      consola__default['default'].warn('Using alias is deprecated and will be removed in Nuxt 3. Use `isAlias` instead.');
    }

    let lastError;

    // Try to resolve path
    try {
      resolvedPath = this.resolvePath(path, { isAlias, paths });
    } catch (e) {
      lastError = e;
    }

    const isExternal = utils.isExternalDependency(resolvedPath);

    // in dev mode make sure to clear the require cache so after
    // a dev server restart any changed file is reloaded
    if (this.options.dev && !isExternal) {
      utils.clearRequireCache(resolvedPath);
    }

    // Try to require
    try {
      requiredModule = this._require(resolvedPath);
    } catch (e) {
      lastError = e;
    }

    // Interop default
    if (interopDefault !== false && requiredModule && requiredModule.default) {
      requiredModule = requiredModule.default;
    }

    // Throw error if failed to require
    if (requiredModule === undefined && lastError) {
      throw lastError
    }

    return requiredModule
  }
}

class Nuxt extends Hookable__default['default'] {
  constructor (options = {}) {
    super(consola__default['default']);

    // Assign options and apply defaults
    this.options = config.getNuxtConfig(options);

    // Create instance of core components
    this.resolver = new Resolver(this);
    this.moduleContainer = new ModuleContainer(this);

    // Deprecated hooks
    this.deprecateHooks({
      // #3294 - 7514db73b25c23b8c14ebdafbb4e129ac282aabd
      'render:context': {
        to: '_render:context',
        message: '`render:context(nuxt)` is deprecated, Please use `vue-renderer:ssr:context(context)`'
      },
      // #3773
      'render:routeContext': {
        to: '_render:context',
        message: '`render:routeContext(nuxt)` is deprecated, Please use `vue-renderer:ssr:context(context)`'
      },
      showReady: 'webpack:done',
      // Introduced in 2.13
      'export:done': 'generate:done',
      'export:before': 'generate:before',
      'export:extendRoutes': 'generate:extendRoutes',
      'export:distRemoved': 'generate:distRemoved',
      'export:distCopied': 'generate:distCopied',
      'export:route': 'generate:route',
      'export:routeFailed': 'generate:routeFailed',
      'export:page': 'generate:page',
      'export:routeCreated': 'generate:routeCreated'
    });

    // Add Legacy aliases
    utils.defineAlias(this, this.resolver, ['resolveAlias', 'resolvePath']);
    this.showReady = () => { this.callHook('webpack:done'); };

    // Init server
    if (this.options.server !== false) {
      this._initServer();
    }

    // Call ready
    if (this.options._ready !== false) {
      this.ready().catch((err) => {
        consola__default['default'].fatal(err);
      });
    }
  }

  static get version () {
    return `v${version}` + (global.__NUXT_DEV__ ? '-development' : '')
  }

  ready () {
    if (!this._ready) {
      this._ready = this._init();
    }
    return this._ready
  }

  async _init () {
    if (this._initCalled) {
      return this
    }
    this._initCalled = true;

    // Add hooks
    if (lodash.isPlainObject(this.options.hooks)) {
      this.addHooks(this.options.hooks);
    } else if (typeof this.options.hooks === 'function') {
      this.options.hooks(this.hook);
    }

    // Await for modules
    await this.moduleContainer.ready();

    // Await for server to be ready
    if (this.server) {
      await this.server.ready();
    }

    // Call ready hook
    await this.callHook('ready', this);

    return this
  }

  _initServer () {
    if (this.server) {
      return
    }
    this.server = new server.Server(this);
    this.renderer = this.server;
    this.render = this.server.app;
    utils.defineAlias(this, this.server, ['renderRoute', 'renderAndGetWindow', 'listen']);
  }

  async close (callback) {
    await this.callHook('close', this);

    if (typeof callback === 'function') {
      await callback();
    }

    this.clearHooks();
  }
}

const OVERRIDES = {
  dry: { dev: false, server: false },
  dev: { dev: true, _build: true },
  build: { dev: false, server: false, _build: true },
  start: { dev: false, _start: true }
};

async function loadNuxt (loadOptions) {
  // Normalize loadOptions
  if (typeof loadOptions === 'string') {
    loadOptions = { for: loadOptions };
  }
  const { ready = true } = loadOptions;
  const _for = loadOptions.for || 'dry';

  // Get overrides
  const override = OVERRIDES[_for];

  // Unsupported purpose
  if (!override) {
    throw new Error('Unsupported for: ' + _for)
  }

  // Load Config
  const config$1 = await config.loadNuxtConfig(loadOptions);

  // Apply config overrides
  Object.assign(config$1, override);

  // Initiate Nuxt
  const nuxt = new Nuxt(config$1);
  if (ready) {
    await nuxt.ready();
  }

  return nuxt
}

Object.defineProperty(exports, 'loadNuxtConfig', {
  enumerable: true,
  get: function () {
    return config.loadNuxtConfig;
  }
});
exports.Module = ModuleContainer;
exports.Nuxt = Nuxt;
exports.Resolver = Resolver;
exports.loadNuxt = loadNuxt;
