docs: @Override
    protected void visitSoyFileNode(SoyFileNode node) {
      StringBuilder file = new StringBuilder();
  
      file.append("// This file was automatically generated from ")
          .append(node.getFileName())
          .append(".\n");
      file.append("// Please don't edit this file by hand.\n");
  
      // Output a section containing optionally-parsed compiler directives in comments. Since these
      // are comments, they are not controlled by an option, and will be removed by minifiers that do
      // not understand them.
      file.append("\n");
      String fileOverviewDescription = "Templates in namespace " + node.getNamespace() + ".";
      JsDoc.Builder jsDocBuilder = JsDoc.builder();
      jsDocBuilder.addAnnotation("fileoverview", fileOverviewDescription);
      if (node.getDelPackageName() != null) {
        jsDocBuilder.addParameterizedAnnotation("modName", node.getDelPackageName());
      }
      addJsDocToProvideDelTemplates(jsDocBuilder, node);
      addJsDocToRequireDelTemplates(jsDocBuilder, node);
      addCodeToRequireCss(jsDocBuilder, node);
      jsDocBuilder.addAnnotation("public");
      file.append(jsDocBuilder.build());
      file.append("\n\n");
  
      // Add code to define JS namespaces or add provide/require calls for Closure Library.
      templateAliases = AliasUtils.IDENTITY_ALIASES;
      jsCodeBuilder = createCodeBuilder();
  
      if (jsSrcOptions.shouldGenerateGoogModules()) {
        templateAliases = AliasUtils.createTemplateAliases(node);
  
        addCodeToDeclareGoogModule(file, node);
        addCodeToRequireGoogModules(node);
      } else if (jsSrcOptions.shouldProvideRequireSoyNamespaces()) {
        addCodeToProvideSoyNamespace(file, node);
        file.append('\n');
        addCodeToRequireSoyNamespaces(node);
      } else {
        throw new AssertionError("impossible");
      }
  
      // add declarations for the ijdata params
      // This takes advantage of the way '@record' types in closure are 'open'.
      // Unfortunately clutz doesn't understand this:
      // https://github.com/angular/clutz/issues/832
      // So TS users will not be able to see the property definitions.
      // The most practical solution to that is for soy to generate its own .d.ts files.
      Map<String, SoyType> ijData = getAllIjDataParams(node);
      if (!ijData.isEmpty()) {
        GoogRequire require = GoogRequire.create("soy");
        jsCodeBuilder.appendLine();
        for (Map.Entry<String, SoyType> entry : ijData.entrySet()) {
          jsCodeBuilder.appendLine();
          //
          jsCodeBuilder.appendLine(
              JsDoc.builder()
                  // Because every declaration can declare a type, we can get errors if they don't
                  // declare identical types.  There isn't a good way to force identical declarations
                  // so we just suppress the duplicate error warning.
                  .addParameterizedAnnotation("suppress", "duplicate")
                  // declare every field as optional.  This is because if a template is unused and
                  // declares an ij param we don't want to force people to supply a value.
                  .addParameterizedAnnotation(
                      "type",
                      getJsTypeForParamForDeclaration(entry.getValue()).typeExpr() + "|undefined")
                  .build()
                  .toString());
          jsCodeBuilder.append(
              require
                  .reference()
                  .dotAccess("IjData")
                  .dotAccess("prototype")
                  .dotAccess(entry.getKey())
                  .asStatement());
        }
      }
  
      // Add code for each template.
      for (TemplateNode template : node.getChildren()) {
        jsCodeBuilder.appendLine().appendLine();
        staticVarDeclarations = new ArrayList<>();
        visit(template);
        if (!staticVarDeclarations.isEmpty()) {
          jsCodeBuilder.append(Statement.of(staticVarDeclarations));
        }
      }
      jsCodeBuilder.appendGoogRequiresTo(file);
      jsCodeBuilder.appendCodeTo(file);
      jsFilesContents.add(file.toString());
      jsCodeBuilder = null;
    }