scripts/include.go GO 156 lines View on github.com → Search inside
1// SPDX-License-Identifier: MIT23package main45import (6	"bytes"7	_ "embed"8	"fmt"9	"go/format"10	"maps"11	"os"12	"regexp"13	"slices"14	"strconv"15	"strings"16	"text/template"1718	"github.com/boyter/scc/v4/processor"19	jsoniter "github.com/json-iterator/go"20)2122const (23	constantsFile     = "./processor/constants.go"24	languagesListFile = "./LANGUAGES.md"25)2627var json = jsoniter.ConfigCompatibleWithStandardLibrary2829//go:embed languages.tmpl30var langTemplate string3132// loadLanguages reads and validates all languages*.json files in the current33// folder, returning the merged language database. Both generated artifacts are34// produced from this single freshly parsed map so that one generation pass is35// fully consistent. Previously generateLanguagesList read the languageDatabase36// compiled into this binary, which is the *old* constants.go (compiled before37// this run rewrote it), so LANGUAGES.md always lagged the JSON by one pass.38func loadLanguages() (map[string]processor.Language, error) {39	files, _ := os.ReadDir(".")40	langs := map[string]processor.Language{}4142	for _, f := range files {43		if strings.HasPrefix(f.Name(), "languages") && strings.HasSuffix(f.Name(), ".json") {44			file, err := os.Open(f.Name())45			if err != nil {46				return nil, fmt.Errorf("failed to open file '%s': %v", f.Name(), err)47			}4849			data := map[string]processor.Language{}5051			// validate the json by decoding into an empty struct52			if err := json.NewDecoder(file).Decode(&data); err != nil {53				_ = file.Close()54				return nil, fmt.Errorf("failed to validate json in file '%s': %v", f.Name(), err)55			}56			_ = file.Close()5758			// validate that every regex heuristic compiles ahead of time so a59			// broken pattern fails the build rather than at runtime60			for name, lang := range data {61				for _, h := range lang.Heuristics {62					if _, err := regexp.Compile(h.Pattern); err != nil {63						return nil, fmt.Errorf("invalid heuristic regex %q for language '%s' in file '%s': %v", h.Pattern, name, f.Name(), err)64					}65				}66			}6768			maps.Insert(langs, maps.All(data))69		}70	}7172	return langs, nil73}7475// encodes the language database as string literals in constants.go76func generateConstants(langs map[string]processor.Language) error {77	buf := &bytes.Buffer{}7879	t, err := template.New("codeGenerator").Funcs(template.FuncMap{80		"quote": strconv.Quote,81	}).Parse(langTemplate)82	if err != nil {83		return fmt.Errorf("failed to parse template file: %v", err)84	}8586	if err := t.Execute(buf, langs); err != nil {87		return fmt.Errorf("failed to execute template file: %v", err)88	}8990	source, err := format.Source(buf.Bytes())91	if err != nil {92		return fmt.Errorf("failed to format code: %v", err)93	}9495	out, err := os.OpenFile(constantsFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)96	if err != nil {97		return fmt.Errorf("failed to open constants file: %v", err)98	}99	defer func(file *os.File) {100		_ = file.Close()101	}(out)102103	if _, err := out.Write(source); err != nil {104		return fmt.Errorf("failed to write constants file %v", err)105	}106107	return nil108}109110// generateLanguagesList writes LANGUAGES.md from the freshly parsed language111// map. It mirrors the formatting of processor.PrintLanguages but works on the112// passed in data rather than the compiled-in languageDatabase so a single113// generation pass stays consistent with constants.go.114func generateLanguagesList(langs map[string]processor.Language) error {115	out, err := os.OpenFile(languagesListFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)116	if err != nil {117		return fmt.Errorf("failed to open languages list file: %v", err)118	}119	defer func(file *os.File) {120		_ = file.Close()121	}(out)122123	names := make([]string, 0, len(langs))124	for name := range langs {125		names = append(names, name)126	}127	slices.SortFunc(names, func(a, b string) int {128		return strings.Compare(strings.ToLower(a), strings.ToLower(b))129	})130131	_, _ = out.WriteString("```\n")132	for _, name := range names {133		exts := append(append([]string{}, langs[name].Extensions...), langs[name].FileNames...)134		_, _ = fmt.Fprintf(out, "%s (%s)\n", name, strings.Join(exts, ","))135	}136	_, _ = out.WriteString("```\n")137138	return nil139}140141func main() {142	langs, err := loadLanguages()143	if err != nil {144		fmt.Fprintf(os.Stderr, "failed to load languages: %v\n", err)145		os.Exit(1)146	}147	if err := generateConstants(langs); err != nil {148		fmt.Fprintf(os.Stderr, "failed to generate constants: %v\n", err)149		os.Exit(1)150	}151	if err := generateLanguagesList(langs); err != nil {152		fmt.Fprintf(os.Stderr, "failed to generate languages list: %v\n", err)153		os.Exit(1)154	}155}

Code quality findings 6

Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
warning correctness defer-in-loop
defer func(file *os.File) {
Blank identifier discarding results; verify intentional ignoring of return values
warning correctness blank-identifier-discard
_, _ = out.WriteString("```\n")
Blank identifier discarding results; verify intentional ignoring of return values
warning correctness blank-identifier-discard
_, _ = fmt.Fprintf(out, "%s (%s)\n", name, strings.Join(exts, ","))
Blank identifier discarding results; verify intentional ignoring of return values
warning correctness blank-identifier-discard
_, _ = out.WriteString("```\n")
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if err := json.NewDecoder(file).Decode(&data); err != nil {
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for name, lang := range data {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.