"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.EntryResolver = void 0;
exports.default = createEntryRequest;
function _utils() {
  const data = require("@parcel/utils");
  _utils = function () {
    return data;
  };
  return data;
}
function _diagnostic() {
  const data = _interopRequireWildcard(require("@parcel/diagnostic"));
  _diagnostic = function () {
    return data;
  };
  return data;
}
function _path() {
  const data = _interopRequireDefault(require("path"));
  _path = function () {
    return data;
  };
  return data;
}
function _jsonSourcemap() {
  const data = require("@mischnic/json-sourcemap");
  _jsonSourcemap = function () {
    return data;
  };
  return data;
}
var _RequestTracker = require("../RequestTracker");
var _projectPath = require("../projectPath");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
const type = 'entry_request';
function createEntryRequest(input) {
  return {
    id: `${type}:${(0, _projectPath.fromProjectPathRelative)(input)}`,
    type: _RequestTracker.requestTypes.entry_request,
    run,
    input
  };
}
async function run({
  input,
  api,
  options
}) {
  let entryResolver = new EntryResolver(options);
  let filePath = (0, _projectPath.fromProjectPath)(options.projectRoot, input);
  let result = await entryResolver.resolveEntry(filePath);

  // Connect files like package.json that affect the entry
  // resolution so we invalidate when they change.
  for (let file of result.files) {
    api.invalidateOnFileUpdate(file.filePath);
    api.invalidateOnFileDelete(file.filePath);
  }

  // If the entry specifier is a glob, add a glob node so
  // we invalidate when a new file matches.
  for (let glob of result.globs) {
    api.invalidateOnFileCreate({
      glob: (0, _projectPath.toProjectPath)(options.projectRoot, glob)
    });
  }

  // Invalidate whenever an entry is deleted.
  // If the entry was a glob, we'll re-evaluate it, and otherwise
  // a proper entry error will be thrown.
  for (let entry of result.entries) {
    api.invalidateOnFileDelete(entry.filePath);
  }
  return result;
}
async function assertFile(fs, entry, relativeSource, pkgFilePath, keyPath, options) {
  let source = _path().default.join(entry, relativeSource);
  let stat;
  try {
    stat = await fs.stat(source);
  } catch (err) {
    let contents = await fs.readFile(pkgFilePath, 'utf8');
    let alternatives = await (0, _utils().findAlternativeFiles)(fs, relativeSource, entry, options.projectRoot, false);
    throw new (_diagnostic().default)({
      diagnostic: {
        origin: '@parcel/core',
        message: (0, _diagnostic().md)`${_path().default.relative(process.cwd(), source)} does not exist.`,
        codeFrames: [{
          filePath: pkgFilePath,
          codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(contents, [{
            key: keyPath,
            type: 'value'
          }])
        }],
        hints: alternatives.map(r => {
          return (0, _diagnostic().md)`Did you mean '__${r}__'?`;
        })
      }
    });
  }
  if (!stat.isFile()) {
    let contents = await fs.readFile(pkgFilePath, 'utf8');
    throw new (_diagnostic().default)({
      diagnostic: {
        origin: '@parcel/core',
        message: (0, _diagnostic().md)`${_path().default.relative(process.cwd(), source)} is not a file.`,
        codeFrames: [{
          filePath: pkgFilePath,
          codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(contents, [{
            key: keyPath,
            type: 'value'
          }])
        }]
      }
    });
  }
}
class EntryResolver {
  constructor(options) {
    this.options = options;
  }
  async resolveEntry(entry) {
    let stat;
    try {
      stat = await this.options.inputFS.stat(entry);
    } catch (err) {
      if (!(0, _utils().isGlob)(entry)) {
        throw new (_diagnostic().default)({
          diagnostic: {
            message: (0, _diagnostic().md)`Entry ${entry} does not exist`
          }
        });
      }
      let files = await (0, _utils().glob)(entry, this.options.inputFS, {
        absolute: true,
        onlyFiles: false
      });
      let results = await Promise.all(files.map(f => this.resolveEntry(_path().default.normalize(f))));
      return results.reduce((p, res) => ({
        entries: p.entries.concat(res.entries),
        files: p.files.concat(res.files),
        globs: p.globs.concat(res.globs)
      }), {
        entries: [],
        files: [],
        globs: [entry]
      });
    }
    if (stat.isDirectory()) {
      let pkg = await this.readPackage(entry);
      if (pkg) {
        let {
          filePath
        } = pkg;
        let entries = [];
        let files = [{
          filePath: (0, _projectPath.toProjectPath)(this.options.projectRoot, filePath)
        }];
        let globs = [];
        let targetsWithSources = 0;
        if (pkg.targets) {
          for (let targetName in pkg.targets) {
            let target = pkg.targets[targetName];
            if (target.source != null) {
              targetsWithSources++;
              let targetSources = Array.isArray(target.source) ? target.source : [target.source];
              let i = 0;
              for (let source of targetSources) {
                let sources;
                if ((0, _utils().isGlob)(source)) {
                  globs.push(source);
                  sources = await (0, _utils().glob)(source, this.options.inputFS, {
                    onlyFiles: true,
                    cwd: entry
                  });
                } else {
                  sources = [source];
                }
                let keyPath = `/targets/${targetName}/source${Array.isArray(target.source) ? `/${i}` : ''}`;
                for (let relativeSource of sources) {
                  let source = _path().default.join(entry, relativeSource);
                  await assertFile(this.options.inputFS, entry, relativeSource, filePath, keyPath, this.options);
                  entries.push({
                    filePath: (0, _projectPath.toProjectPath)(this.options.projectRoot, source),
                    packagePath: (0, _projectPath.toProjectPath)(this.options.projectRoot, entry),
                    target: targetName,
                    loc: {
                      filePath: (0, _projectPath.toProjectPath)(this.options.projectRoot, pkg.filePath),
                      ...(0, _diagnostic().getJSONSourceLocation)(pkg.map.pointers[keyPath], 'value')
                    }
                  });
                }
                i++;
              }
            }
          }
        }
        let allTargetsHaveSource = targetsWithSources > 0 && pkg != null && pkg.targets != null && Object.keys(pkg.targets).length === targetsWithSources;
        if (!allTargetsHaveSource && pkg.source != null) {
          let pkgSources = Array.isArray(pkg.source) ? pkg.source : [pkg.source];
          let i = 0;
          for (let pkgSource of pkgSources) {
            let sources;
            if ((0, _utils().isGlob)(pkgSource)) {
              globs.push(pkgSource);
              sources = await (0, _utils().glob)(pkgSource, this.options.inputFS, {
                onlyFiles: true,
                cwd: _path().default.dirname(filePath)
              });
            } else {
              sources = [pkgSource];
            }
            let keyPath = `/source${Array.isArray(pkg.source) ? `/${i}` : ''}`;
            for (let relativeSource of sources) {
              let source = _path().default.join(_path().default.dirname(filePath), relativeSource);
              await assertFile(this.options.inputFS, entry, relativeSource, filePath, keyPath, this.options);
              entries.push({
                filePath: (0, _projectPath.toProjectPath)(this.options.projectRoot, source),
                packagePath: (0, _projectPath.toProjectPath)(this.options.projectRoot, entry),
                loc: {
                  filePath: (0, _projectPath.toProjectPath)(this.options.projectRoot, pkg.filePath),
                  ...(0, _diagnostic().getJSONSourceLocation)(pkg.map.pointers[keyPath], 'value')
                }
              });
            }
            i++;
          }
        }

        // Only return if we found any valid entries
        if (entries.length && files.length) {
          return {
            entries,
            files,
            globs
          };
        }
      }
      throw new (_diagnostic().default)({
        diagnostic: {
          message: (0, _diagnostic().md)`Could not find entry: ${entry}`
        }
      });
    } else if (stat.isFile()) {
      let projectRoot = this.options.projectRoot;
      let packagePath = (0, _utils().isDirectoryInside)(this.options.inputFS.cwd(), projectRoot) ? this.options.inputFS.cwd() : projectRoot;
      return {
        entries: [{
          filePath: (0, _projectPath.toProjectPath)(this.options.projectRoot, entry),
          packagePath: (0, _projectPath.toProjectPath)(this.options.projectRoot, packagePath)
        }],
        files: [],
        globs: []
      };
    }
    throw new (_diagnostic().default)({
      diagnostic: {
        message: (0, _diagnostic().md)`Unknown entry: ${entry}`
      }
    });
  }
  async readPackage(entry) {
    let content, pkg;
    let pkgFile = _path().default.join(entry, 'package.json');
    try {
      content = await this.options.inputFS.readFile(pkgFile, 'utf8');
    } catch (err) {
      return null;
    }
    try {
      pkg = JSON.parse(content);
    } catch (err) {
      // TODO: code frame?
      throw new (_diagnostic().default)({
        diagnostic: {
          message: (0, _diagnostic().md)`Error parsing ${_path().default.relative(this.options.inputFS.cwd(), pkgFile)}: ${err.message}`
        }
      });
    }
    return {
      ...pkg,
      filePath: pkgFile,
      map: (0, _jsonSourcemap().parse)(content, undefined, {
        tabWidth: 1
      })
    };
  }
}
exports.EntryResolver = EntryResolver;