Optional JSParserImpl::parseDeclareModuleFlow()

in lib/Parser/JSParserImpl-flow.cpp [373:526]


Optional<ESTree::Node *> JSParserImpl::parseDeclareModuleFlow(SMLoc start) {
  assert(check(moduleIdent_));
  advance(JSLexer::GrammarContext::Type);

  if (checkAndEat(TokenKind::period, JSLexer::GrammarContext::Type)) {
    if (!checkAndEat(exportsIdent_, JSLexer::GrammarContext::Type)) {
      error(tok_->getSourceRange(), "expected module.exports declaration");
      return None;
    }

    SMLoc annotStart = tok_->getStartLoc();
    if (!eat(
            TokenKind::colon,
            JSLexer::GrammarContext::Type,
            "in module.exports declaration",
            "start of declaration",
            start))
      return None;
    auto optType = parseTypeAnnotationFlow(annotStart);
    if (!optType)
      return None;
    eatSemi(true);
    return setLocation(
        start,
        getPrevTokenEndLoc(),
        new (context_) ESTree::DeclareModuleExportsNode(*optType));
  }

  // declare module Identifier {[opt]
  //                ^
  ESTree::Node *id = nullptr;
  if (check(TokenKind::string_literal)) {
    id = setLocation(
        tok_,
        tok_,
        new (context_) ESTree::StringLiteralNode(tok_->getStringLiteral()));
  } else {
    if (!need(
            TokenKind::identifier,
            "in module declaration",
            "start of declaration",
            start))
      return None;
    id = setLocation(
        tok_,
        tok_,
        new (context_)
            ESTree::IdentifierNode(tok_->getIdentifier(), nullptr, false));
  }
  advance(JSLexer::GrammarContext::Type);

  // declare module Identifier {
  //                           ^
  SMLoc bodyStart = tok_->getStartLoc();
  if (!eat(
          TokenKind::l_brace,
          JSLexer::GrammarContext::Type,
          "in module declaration",
          "start of declaration",
          start))
    return None;

  UniqueString *kind = nullptr;
  ESTree::NodeList declarations{};

  while (!check(TokenKind::r_brace)) {
    if (check(TokenKind::rw_import)) {
      // 'import' can be bare without a 'declare' before it.
      auto optImport = parseImportDeclaration();
      if (!optImport)
        return None;
      ESTree::ImportDeclarationNode *import = *optImport;
      if (import->_importKind == valueIdent_) {
        error(
            import->getSourceRange(),
            "imports within a `declare module` body must always be "
            "`import type` or `import typeof`");
      }
      declarations.push_back(*import);
      continue;
    }
    if (!check(declareIdent_)) {
      error(
          tok_->getSourceRange(),
          "expected 'declare' in module declaration body");
      return None;
    }
    SMLoc declarationStart = advance(JSLexer::GrammarContext::Type).Start;
    auto optDecl =
        parseDeclareFLow(declarationStart, AllowDeclareExportType::Yes);
    if (!optDecl)
      return None;
    ESTree::Node *decl = *optDecl;
    switch (decl->getKind()) {
      case ESTree::NodeKind::DeclareModuleExports:
        if (kind != nullptr && kind != commonJSIdent_) {
          error(
              decl->getSourceRange(),
              "cannot use CommonJS export in ES module");
        }
        kind = commonJSIdent_;
        break;
      case ESTree::NodeKind::DeclareExportDeclaration:
        if (llvh::dyn_cast_or_null<ESTree::InterfaceDeclarationNode>(
                llvh::cast<ESTree::DeclareExportDeclarationNode>(decl)
                    ->_declaration)) {
          // declare export interface can show up in either module kind.
          // Ignore it.
          break;
        }
        if (llvh::dyn_cast_or_null<ESTree::TypeAliasNode>(
                llvh::cast<ESTree::DeclareExportDeclarationNode>(decl)
                    ->_declaration)) {
          // declare export type can show up in either module kind.
          // Ignore it.
          break;
        }
        if (kind != nullptr && kind != esIdent_) {
          error(
              decl->getSourceRange(),
              "cannot use ESM export in CommonJS module");
        }
        kind = esIdent_;
        break;
      case ESTree::NodeKind::DeclareExportAllDeclaration:
        if (kind != nullptr && kind != esIdent_) {
          error(
              decl->getSourceRange(),
              "cannot use ESM export in CommonJS module");
        }
        kind = esIdent_;
        break;
      default:
        break;
    }
    declarations.push_back(*decl);
  }

  SMLoc bodyEnd = advance(JSLexer::GrammarContext::Type).End;

  ESTree::Node *body = setLocation(
      bodyStart,
      bodyEnd,
      new (context_) ESTree::BlockStatementNode(std::move(declarations)));

  if (kind == nullptr) {
    // Default to CommonJS if we weren't able to figure it out based on
    // declarations themselves.
    kind = commonJSIdent_;
  }

  return setLocation(
      start, body, new (context_) ESTree::DeclareModuleNode(id, body, kind));
}