Skip to content

perf(templates): rm double parsing in template loading#6796

Merged
dwisiswant0 merged 1 commit intodevfrom
dwisiswant0/perf/templates/rm-double-parsing-in-template-loading
Feb 4, 2026
Merged

perf(templates): rm double parsing in template loading#6796
dwisiswant0 merged 1 commit intodevfrom
dwisiswant0/perf/templates/rm-double-parsing-in-template-loading

Conversation

@dwisiswant0
Copy link
Member

@dwisiswant0 dwisiswant0 commented Feb 1, 2026

Proposed changes

perf(templates): rm double parsing in template loading

Refactor ParseTemplateFromReader to parse YAML
once after applying preprocessors, avoiding
redundant parsing for verification. Also add
parseTemplateNoVerify and
applyTemplateVerification helpers to separate
parsing from signature verification logic to
reduce CPU overhead during startup template
loading.

Proof

Key memory improvements:

YAML parsing:

  • yaml.(*parser).node: -15.41MB (-2.24%)
  • yaml.(*parser).scalar: -2.15MB
  • yaml.(*parser).mapping: -0.77MB (cum -19.39MB)
  • yaml.(*parser).sequence: -0.39MB (cum -19.23MB)

Reflection ops:

  • reflect.New: -1.51MB
  • reflect.MakeSlice: -113.55KB
  • reflect.MakeMap: -60.35KB
  • reflect.growslice: -296.05KB
  • reflect.SetMapIndex: -66.27KB

Template processing:

  • parseTemplate: -8MB flat (cum -25.45MB)
  • ParseTemplateFromReader: -17.91MB cum
  • Preprocessing (randStrPreprocessor): -35.81KB (cum -1.32MB)
  • bytes.Replace: -1.27MB (less template data manipulation)

Mixed result:

  • parseTemplateNoVerify: +8.09MB flat but still achieves overall reductions through downstream savings
go tool pprof
$ go tool pprof -top -nodecount 20 -base 3.7.0.mem patch.mem
File: nuclei-patch
Build ID: 6d708c49384e678a966d455e7525b7a3e9d61902
Type: inuse_space
Time: 2026-02-02 01:59:06 WIB
Showing nodes accounting for -18.79MB, 2.73% of 688.21MB total
Dropped 585 nodes (cum <= 3.44MB)
Showing top 20 nodes out of 175
      flat  flat%   sum%        cum   cum%
  -15.41MB  2.24%  2.24%   -15.41MB  2.24%  gopkg.in/yaml%2ev2.(*parser).node (inline)
    8.09MB  1.18%  1.06%   202.57MB 29.43%  github.com/projectdiscovery/nuclei/v3/pkg/templates.parseTemplateNoVerify
      -8MB  1.16%  2.23%   -25.45MB  3.70%  github.com/projectdiscovery/nuclei/v3/pkg/templates.parseTemplate
   -2.15MB  0.31%  2.54%   -14.21MB  2.06%  gopkg.in/yaml%2ev2.(*parser).scalar
   -1.51MB  0.22%  2.76%    -1.51MB  0.22%  reflect.New
    1.38MB   0.2%  2.56%     1.38MB   0.2%  io.ReadAll
   -1.27MB  0.18%  2.74%    -1.27MB  0.18%  bytes.Replace
   -0.77MB  0.11%  2.85%   -19.39MB  2.82%  gopkg.in/yaml%2ev2.(*parser).mapping
   -0.65MB 0.094%  2.95%    -0.65MB 0.094%  gopkg.in/yaml%2ev2.read
    0.59MB 0.085%  2.86%     0.59MB 0.085%  regexp/syntax.(*compiler).inst (inline)
    0.53MB 0.078%  2.78%     0.53MB 0.078%  encoding/gob.decString
    0.45MB 0.065%  2.72%     0.38MB 0.055%  github.com/projectdiscovery/retryablehttp-go.DefaultReusePooledTransport
    0.42MB 0.061%  2.66%        1MB  0.14%  github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/httpclientpool.wrappedGet
   -0.39MB 0.057%  2.72%   -19.23MB  2.79%  gopkg.in/yaml%2ev2.(*parser).sequence
   -0.29MB 0.042%  2.76%    -0.29MB 0.042%  reflect.growslice
   -0.21MB 0.031%  2.79%    -0.18MB 0.026%  github.com/Knetic/govaluate.parseTokens
    0.20MB  0.03%  2.76%     0.20MB  0.03%  net/http.(*Transport).RegisterProtocol
    0.19MB 0.027%  2.73%     0.19MB 0.027%  github.com/bytedance/sonic/internal/caching.newProgramMap
    0.17MB 0.025%  2.71%     0.21MB 0.031%  github.com/Knetic/govaluate.planValue
   -0.17MB 0.025%  2.73%    -0.17MB 0.025%  github.com/projectdiscovery/nuclei/v3/pkg/protocols.(*ExecutorOptions).Copy
$ go tool pprof -list "reflect\." -base 3.7.0.mem patch.mem
Total: 688.21MB
ROUTINE ======================== reflect.(*rtype).ptrTo in /usr/local/go/src/reflect/type.go
   20.16kB    40.38kB (flat, cum) 0.0057% of Total
         .          .  
   20.16kB    20.16kB   1371: pp := *prototype
         .       -56B   1373: pp.Str = resolveReflectName(newName(s, "", false, false))
         .    20.28kB   1385: pi, _ := ptrMap.LoadOrStore(t, &pp)
ROUTINE ======================== reflect.MakeMap in /usr/local/go/src/reflect/value.go
         0   -60.35kB (flat, cum) 0.0086% of Total
         .   -60.35kB   3024: return MakeMapWithSize(typ, 0)
ROUTINE ======================== reflect.MakeMapWithSize in /usr/local/go/src/reflect/value.go
  -60.35kB   -60.35kB (flat, cum) 0.0086% of Total
  -60.35kB   -60.35kB   3034: m := makemap(t, n)
ROUTINE ======================== reflect.MakeSlice in /usr/local/go/src/reflect/value.go
         0  -113.55kB (flat, cum) 0.016% of Total
         .  -113.55kB   2992: s := unsafeheader.Slice{Data: unsafe_NewArray(&(typ.Elem().(*rtype).t), cap), Len: len, Cap: cap}
ROUTINE ======================== reflect.New in /usr/local/go/src/reflect/value.go
   -1.51MB    -1.51MB (flat, cum)  0.22% of Total
   -1.51MB    -1.51MB   3095: ptr := unsafe_New(t)
ROUTINE ======================== reflect.PointerTo in /usr/local/go/src/reflect/type.go
         0    28.20kB (flat, cum) 0.004% of Total
         .    28.20kB   1342: return toRType(t.(*rtype).ptrTo())
ROUTINE ======================== reflect.PtrTo in /usr/local/go/src/reflect/type.go
         0    28.20kB (flat, cum) 0.004% of Total
         .    28.20kB   1337:func PtrTo(t Type) Type { return PointerTo(t) }
ROUTINE ======================== reflect.Value.Addr in /usr/local/go/src/reflect/value.go
         0    12.19kB (flat, cum) 0.0017% of Total
         .    12.19kB    273: return Value{ptrTo(v.typ()), v.ptr, fl | flag(Pointer)}
ROUTINE ======================== reflect.Value.Grow in /usr/local/go/src/reflect/value.go
         0  -296.05kB (flat, cum) 0.042% of Total
         .  -296.05kB   2664: v.grow(n)
ROUTINE ======================== reflect.Value.Set in /usr/local/go/src/reflect/value.go
         0   -48.14kB (flat, cum) 0.0068% of Total
         .   -48.14kB   2131: x = x.assignTo("reflect.Set", v.typ(), target)
ROUTINE ======================== reflect.Value.SetMapIndex in /usr/local/go/src/reflect/map_swiss.go
         0   -66.27kB (flat, cum) 0.0094% of Total
         .   -62.13kB    427:   mapassign_faststr(v.typ(), v.pointer(), k, e)
         .    -4.14kB    450: mapassign(v.typ(), v.pointer(), k, e)
ROUTINE ======================== reflect.Value.assignTo in /usr/local/go/src/reflect/value.go
         0   -48.14kB (flat, cum) 0.0068% of Total
         .   -48.14kB   3133:   x := valueInterface(v, false)
ROUTINE ======================== reflect.Value.grow in /usr/local/go/src/reflect/value.go
         0  -296.05kB (flat, cum) 0.042% of Total
         .  -296.05kB   2677:   *p = growslice(t, *p, n)
ROUTINE ======================== reflect.addReflectOff in /usr/local/go/src/runtime/runtime1.go
         0          0 (flat, cum)     0% of Total
ROUTINE ======================== reflect.growslice in /usr/local/go/src/runtime/slice.go
 -296.05kB  -296.05kB (flat, cum) 0.042% of Total
 -296.05kB  -296.05kB    336: new := growslice(old.array, old.cap+num, old.cap, num, et)
ROUTINE ======================== reflect.mapassign in /usr/local/go/src/reflect/value.go
         0    -4.14kB (flat, cum) 0.00059% of Total
         .    -4.14kB   3677: mapassign0(t, m, key, val)
ROUTINE ======================== reflect.mapassign0 in /usr/local/go/src/runtime/map_swiss.go
   -4.14kB    -4.14kB (flat, cum) 0.00059% of Total
   -4.14kB    -4.14kB    258: p := mapassign(t, m, key)
ROUTINE ======================== reflect.mapassign_faststr in /usr/local/go/src/reflect/value.go
         0   -62.13kB (flat, cum) 0.0088% of Total
         .   -62.13kB   3686: mapassign_faststr0(t, m, key, val)
ROUTINE ======================== reflect.mapassign_faststr0 in /usr/local/go/src/runtime/map_swiss.go
  -62.13kB   -62.13kB (flat, cum) 0.0088% of Total
  -62.13kB   -62.13kB    264: p := mapassign_faststr(t, m, key)
ROUTINE ======================== reflect.newName in /usr/local/go/src/reflect/type.go
         0       -56B (flat, cum) 7.8e-06% of Total
         .       -56B    438: return abi.NewName(n, tag, exported, embedded)
ROUTINE ======================== reflect.packEface in /usr/local/go/src/reflect/value.go
  -48.14kB   -48.14kB (flat, cum) 0.0068% of Total
  -48.14kB   -48.14kB    135:     c := unsafe_New(t)
ROUTINE ======================== reflect.ptrTo in /usr/local/go/src/reflect/type.go
         0    12.19kB (flat, cum) 0.0017% of Total
         .    12.19kB   1390: return toRType(t).ptrTo()
ROUTINE ======================== reflect.resolveReflectName in /usr/local/go/src/reflect/type.go
         0          0 (flat, cum)     0% of Total
ROUTINE ======================== reflect.unsafe_NewArray in /usr/local/go/src/runtime/malloc.go
 -113.55kB  -113.55kB (flat, cum) 0.016% of Total
 -113.55kB  -113.55kB   1816: return newarray(typ, n)
ROUTINE ======================== reflect.valueInterface in /usr/local/go/src/reflect/value.go
         0   -48.14kB (flat, cum) 0.0068% of Total
         .   -48.14kB   1495: return packEface(v)
$ go tool pprof -list "templates\." -base 3.7.0.mem patch.mem
Total: 688.21MB
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.(*Cache).Has in /home/dw1/Development/PD/nuclei/pkg/templates/cache.go
         0     4.05kB (flat, cum) 0.00057% of Total
         .     4.05kB     56: value, ok := t.items.Get(template)
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.(*Cache).StoreWithoutRaw in /home/dw1/Development/PD/nuclei/pkg/templates/cache.go
         0        16B (flat, cum) 2.2e-06% of Total
         .        16B     93: _ = t.items.Set(id, entry)
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.(*Parser).LoadTemplate in /home/dw1/Development/PD/nuclei/pkg/templates/parser.go
         0    -1.16MB (flat, cum)  0.17% of Total
         .    -1.20MB     91: t, templateParseError := p.ParseTemplate(templatePath, catalog)
         .    38.34kB    105: validationError := validateTemplateMandatoryFields(template)
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.(*Parser).ParseTemplate in /home/dw1/Development/PD/nuclei/pkg/templates/parser.go
   22.10kB    -1.20MB (flat, cum)  0.17% of Total
         .     8.09kB    130: value, _, err := p.parsedTemplatesCache.Has(templatePath)
   -4.02kB    -4.02kB    146:   data, err = io.ReadAll(reader)
         .   -38.34kB    150:   data, err = yamlutil.PreProcess(data)
   26.12kB    26.12kB    156: template := &Template{}
         .    -1.19MB    173:       err = yaml.UnmarshalStrict(data, template)
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.(*Template).UnmarshalYAML in /home/dw1/Development/PD/nuclei/pkg/templates/templates.go
         0    -1.71MB (flat, cum)  0.25% of Total
         .    -1.74MB    339: err := unmarshal(alias)
         .    32.01kB    345: if !ReTemplateID.MatchString(template.ID) {
         .     4.05kB    378: err = tplValidator.Struct(template)
         .    20.04kB    388:   err = unmarshal(&tempmap)
         .   -24.19kB    404:   template.addRequestsToQueue(arr...)
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.(*Template).addRequestsToQueue in /home/dw1/Development/PD/nuclei/pkg/templates/templates.go
  -24.19kB   -24.19kB (flat, cum) 0.0034% of Total
  -20.12kB   -20.12kB    519:     template.RequestsQueue = append(template.RequestsQueue, template.convertRequestToProtocolsRequest(template.RequestsHTTP)...)
    8.03kB     8.03kB    521:     template.RequestsQueue = append(template.RequestsQueue, template.convertRequestToProtocolsRequest(template.RequestsHeadless)...)
   -4.04kB    -4.04kB    531:     template.RequestsQueue = append(template.RequestsQueue, template.convertRequestToProtocolsRequest(template.RequestsCode)...)
   -8.06kB    -8.06kB    533:     template.RequestsQueue = append(template.RequestsQueue, template.convertRequestToProtocolsRequest(template.RequestsJavascript)...)
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.(*Template).compileProtocolRequests in /home/dw1/Development/PD/nuclei/pkg/templates/compile.go
  -52.10kB   102.71kB (flat, cum) 0.015% of Total
  -12.02kB   -12.02kB    330:     requests = append(requests, template.convertRequestToProtocolsRequest(template.RequestsNetwork)...)
  -40.08kB   -40.08kB    333:     requests = append(requests, template.convertRequestToProtocolsRequest(template.RequestsHTTP)...)
         .   154.81kB    355: template.Executer, err = tmplexec.NewTemplateExecuter(requests, options)
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.(*Template).validateAllRequestIDs in /home/dw1/Development/PD/nuclei/pkg/templates/templates.go
   20.04kB    20.04kB (flat, cum) 0.0028% of Total
   12.02kB    12.02kB    249:       req.ID = req.Type().String() + "_" + strconv.Itoa(i+1)
    8.02kB     8.02kB    273:       req.ID = req.Type().String() + "_" + strconv.Itoa(i+1)
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.(*randStrPreprocessor).ProcessNReturnData in /home/dw1/Development/PD/nuclei/pkg/templates/preprocessors.go
  -35.81kB    -1.32MB (flat, cum)  0.19% of Total
  -47.83kB   -47.83kB     47: for _, expression := range preprocessorRegex.FindAllStringSubmatch(string(data), -1) {
         .    -8.03kB     61:     randStr := ksuid.New().String()
         .    -1.27MB     62:     data = bytes.ReplaceAll(data, []byte(expression[0]), []byte(randStr))
   12.02kB    12.02kB     63:     dataMap[expression[0]] = randStr
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.Parse in /home/dw1/Development/PD/nuclei/pkg/templates/compile.go
         0   -17.94MB (flat, cum)  2.61% of Total
         .    -4.05kB    171:   if value, _, _ := parser.compiledTemplatesCache.Has(filePath); value != nil {tplCopy.Requests())
         .   -17.94MB    238: return parseFromSource(filePath, preprocessor, options, parser)
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.ParseTemplateFromReader in /home/dw1/Development/PD/nuclei/pkg/templates/compile.go
         0   -17.91MB (flat, cum)  2.60% of Total
         .     1.38MB    415: data, err := io.ReadAll(reader)
         .   -18.24MB    434:   template, err := parseTemplate(data, options)
         .    -2.02MB    452: for _, v := range allPreprocessors {
         .     1.15MB    454:   processedData, replaced = v.ProcessNReturnData(processedData)
         .     7.47MB    460: template, err := parseTemplateNoVerify(processedData, options)
         .   157.17kB    466: template.Constants = generators.MergeMaps(template.Constants, generatedConstants)
         .    -2.47MB    468: applyTemplateVerification(template, data)
         .    -5.19MB    473:     gologger.DefaultLogger.Print().Msgf("[%v] Template %s is not signed or tampered\n", aurora.Yellow("WRN").String(), template.ID)
         .  -153.03kB    478:}
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.init in /home/dw1/Development/PD/nuclei/pkg/templates/validator_singleton.go
         0    -4.01kB (flat, cum) 0.00057% of Total
         .    -4.01kB      7:var tplValidator = validate.New()
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.parseFromSource in /home/dw1/Development/PD/nuclei/pkg/templates/compile.go
         0   -17.91MB (flat, cum)  2.60% of Total
         .   -17.91MB    117: template, err := ParseTemplateFromReader(reader, preprocessor, options)
         .        16B    149:   parser.compiledTemplatesCache.StoreWithoutRaw(filePath, template, err)
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.parseTemplate in /home/dw1/Development/PD/nuclei/pkg/templates/compile.go
      -8MB   -25.45MB (flat, cum)  3.70% of Total
         .   195.10MB    482: template, err := parseTemplateNoVerify(data, srcOptions)
         .    -7.51MB    487:
      -8MB       -8MB    489:}
         .  -123.83MB    498: switch config.GetTemplateFormatFromExt(template.Path) {
         .   -12.02kB    542: if template.Variables.Len() > 0 {
         .    -1.59MB    545:
         .    -3.10MB    566:
         .   -76.50MB    571: }
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.parseTemplateNoVerify in /home/dw1/Development/PD/nuclei/pkg/templates/compile.go
    8.09MB   202.57MB (flat, cum) 29.43% of Total
         .     7.34MB    494: options := srcOptions.Copy()
    8.09MB     8.09MB    496: template := &Template{}
         .   103.93MB    505:   if err = yaml.Unmarshal(data, template); err != nil {
         .    32.06kB    549: template.validateAllRequestIDs()
         .     1.69MB    552: options.CreateTemplateCtxStore()
         .     3.20MB    573: if err := template.compileProtocolRequests(template.Options); err != nil {
         .    78.28MB    578:   if err := template.Executer.Compile(); err != nil {
ROUTINE ======================== github.com/projectdiscovery/nuclei/v3/pkg/templates.validateTemplateMandatoryFields in /home/dw1/Development/PD/nuclei/pkg/templates/parser_validate.go
         0    38.34kB (flat, cum) 0.0054% of Total
         .    38.34kB     28: } else if !ReTemplateID.MatchString(template.ID) {
ROUTINE ======================== github.com/projectdiscovery/utils/maps.(*SyncLockMap[go.shape.string,go.shape.struct { github.com/projectdiscovery/nuclei/v3/pkg/templates.template *github.com/projectdiscovery/nuclei/v3/pkg/templates.Template; github.com/projectdiscovery/nuclei/v3/pkg/templates.raw string; github.com/projectdiscovery/nuclei/v3/pkg/templates.err error; github.com/projectdiscovery/nuclei/v3/pkg/templates.filePath string; github.com/projectdiscovery/nuclei/v3/pkg/templates.modTime time.Time }]).Get in /home/dw1/go/pkg/mod/github.com/projectdiscovery/utils@v0.9.0/maps/synclock_map.go
         0     4.05kB (flat, cum) 0.00057% of Total
         .     4.05kB    172: s.mu.RLock()
ROUTINE ======================== github.com/projectdiscovery/utils/maps.(*SyncLockMap[go.shape.string,go.shape.struct { github.com/projectdiscovery/nuclei/v3/pkg/templates.template *github.com/projectdiscovery/nuclei/v3/pkg/templates.Template; github.com/projectdiscovery/nuclei/v3/pkg/templates.raw string; github.com/projectdiscovery/nuclei/v3/pkg/templates.err error; github.com/projectdiscovery/nuclei/v3/pkg/templates.filePath string; github.com/projectdiscovery/nuclei/v3/pkg/templates.modTime time.Time }]).Set in /home/dw1/go/pkg/mod/github.com/projectdiscovery/utils@v0.9.0/maps/synclock_map.go
    4.06kB        16B (flat, cum) 2.2e-06% of Total
         .    -4.05kB    141: s.mu.Lock()
    4.06kB     4.06kB    161: s.Map[k] = v
ROUTINE ======================== github.com/projectdiscovery/utils/slice.(*SyncSlice[go.shape.*github.com/projectdiscovery/nuclei/v3/pkg/templates.Template]).Append in /home/dw1/go/pkg/mod/github.com/projectdiscovery/utils@v0.9.0/slice/sync_slice.go
         0          0 (flat, cum)     0% of Total

The patch successfully reduces memory allocations by eliminating redundant YAML parsing and reducing reflection overhead during template loading, which shows clear benefits in the YAML parser allocs.

Checklist

  • Pull request is created against the dev branch
  • All checks passed (lint, unit/integration/regression tests etc.) with my changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Summary by CodeRabbit

  • Refactor
    • Reorganized template parsing flow to execute preprocessing before verification, ensuring generated constants are properly integrated.
    • Separated parsing and verification logic for improved clarity and maintainability.
    • Enhanced error handling in the verification workflow.

✏️ Tip: You can customize this high-level summary in your review settings.

Refactor `ParseTemplateFromReader` to parse YAML
once after applying preprocessors, avoiding
redundant parsing for verification. Also add
`parseTemplateNoVerify` and
`applyTemplateVerification` helpers to separate
parsing from signature verification logic to
reduce CPU overhead during startup template
loading.

Signed-off-by: Dwi Siswanto <git@dw1.io>
@auto-assign auto-assign bot requested a review from dogancanbakir February 1, 2026 19:13
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 1, 2026

Walkthrough

Refactored template parsing flow in compile.go to support preprocessing before verification. Preprocessing now executes first, generated constants are accumulated and merged into the template, and verification is applied as a separate post-parse step with updated constants and options incorporated.

Changes

Cohort / File(s) Summary
Template Parsing Refactor
pkg/templates/compile.go
Restructured ParseTemplateFromReader to execute preprocessors first, accumulate and merge generated constants into templates, then perform verification against original data. Introduced internal helpers: parseTemplateNoVerify, parseTemplate, and applyTemplateVerification to separate parsing and verification concerns. Updated error handling and control flow; verification logic moved from preprocessor-first path to dedicated post-parse step.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 ✨ The template flows like garden streams,
Preprocessors bloom with constant dreams,
Verification hops to parse's refrain,
Order perfected, logic made plain! 🌿

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main objective of the pull request: removing double parsing in template loading to improve performance.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dwisiswant0/perf/templates/rm-double-parsing-in-template-loading

Comment @coderabbitai help to get the list of available commands and usage tips.

@dwisiswant0 dwisiswant0 merged commit d642eda into dev Feb 4, 2026
19 checks passed
@dwisiswant0 dwisiswant0 deleted the dwisiswant0/perf/templates/rm-double-parsing-in-template-loading branch February 4, 2026 00:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants