code: func genSearchTypes() error {
  	sourceFile := filepath.Join(pkRoot, filepath.FromSlash("pkg/search/describe.go"))
  	outputFile := filepath.Join(pkRoot, filepath.FromSlash("app/publisher/js/zsearch.go"))
  	fi1, err := os.Stat(sourceFile)
  	if err != nil {
  		return err
  	}
  	fi2, err := os.Stat(outputFile)
  	if err != nil && !os.IsNotExist(err) {
  		return err
  	}
  	if err == nil && fi2.ModTime().After(fi1.ModTime()) {
  		return nil
  	}
  	cmd := exec.Command("go", "generate", "-tags=js", "-v", "perkeep.org/app/publisher/js")
  	if out, err := cmd.CombinedOutput(); err != nil {
  		return fmt.Errorf("go generate for publisher js error: %v, %v", err, string(out))
  	}
  	log.Printf("generated %v", outputFile)
  	return nil
  }
docs: // genSearchTypes duplicates some of the perkeep.org/pkg/search types into
  // perkeep.org/app/publisher/js/zsearch.go , because it's too costly (in output
  // file size) for now to import the search pkg into gopherjs.