diff --git a/cmd/evaluate_all_command.go b/cmd/evaluate_all_command.go index 8bc5ac9b..821cf47b 100644 --- a/cmd/evaluate_all_command.go +++ b/cmd/evaluate_all_command.go @@ -101,12 +101,15 @@ func evaluateAll(cmd *cobra.Command, args []string) (cmdError error) { } if frontMatter != "" { + originalFilename := args[0] frontMatterHandler := yqlib.NewFrontMatterHandler(args[0]) err = frontMatterHandler.Split() if err != nil { return err } args[0] = frontMatterHandler.GetYamlFrontMatterFilename() + yqlib.SetFilenameAlias(args[0], originalFilename) + defer yqlib.ClearFilenameAliases() if frontMatter == "process" { reader := frontMatterHandler.GetContentReader() diff --git a/cmd/evaluate_sequence_command.go b/cmd/evaluate_sequence_command.go index efa9dfa5..4d2ecd72 100644 --- a/cmd/evaluate_sequence_command.go +++ b/cmd/evaluate_sequence_command.go @@ -122,12 +122,15 @@ func evaluateSequence(cmd *cobra.Command, args []string) (cmdError error) { if frontMatter != "" { yqlib.GetLogger().Debug("using front matter handler") + originalFilename := args[0] frontMatterHandler := yqlib.NewFrontMatterHandler(args[0]) err = frontMatterHandler.Split() if err != nil { return err } args[0] = frontMatterHandler.GetYamlFrontMatterFilename() + yqlib.SetFilenameAlias(args[0], originalFilename) + defer yqlib.ClearFilenameAliases() if frontMatter == "process" { reader := frontMatterHandler.GetContentReader() diff --git a/pkg/yqlib/front_matter_test.go b/pkg/yqlib/front_matter_test.go index 47547c51..65c17c50 100644 --- a/pkg/yqlib/front_matter_test.go +++ b/pkg/yqlib/front_matter_test.go @@ -108,6 +108,55 @@ yaml: doc fmHandler.CleanUp() } +func TestFrontMatterFilenamePreserved(t *testing.T) { + // Regression test for https://github.com/mikefarah/yq/issues/2538 + // When using --front-matter, the filename operator should return + // the original filename, not the path to the temporary file. + file := createTestFile(`--- +name: john +--- +Some content +`) + originalFilename := "/path/to/original/file.md" + + fmHandler := NewFrontMatterHandler(file) + err := fmHandler.Split() + if err != nil { + panic(err) + } + + tempFilename := fmHandler.GetYamlFrontMatterFilename() + + // Register the alias (as the command code does) + SetFilenameAlias(tempFilename, originalFilename) + defer ClearFilenameAliases() + + // Verify resolveFilename returns the original name + resolved := resolveFilename(tempFilename) + test.AssertResult(t, originalFilename, resolved) + + // Read documents using the temp file, verify they get the original filename + reader, err := readStream(tempFilename) + if err != nil { + panic(err) + } + decoder := NewYamlDecoder(ConfiguredYamlPreferences) + docs, err := readDocuments(reader, tempFilename, 0, decoder) + if err != nil { + panic(err) + } + + if docs.Len() == 0 { + t.Fatal("expected at least one document") + } + + firstDoc := docs.Front().Value.(*CandidateNode) + test.AssertResult(t, originalFilename, firstDoc.filename) + + tryRemoveTempFile(file) + fmHandler.CleanUp() +} + func TestFrontMatterSplitWithArray(t *testing.T) { file := createTestFile(`[1,2,3] --- diff --git a/pkg/yqlib/stream_evaluator.go b/pkg/yqlib/stream_evaluator.go index 0c9ad6dd..ad147894 100644 --- a/pkg/yqlib/stream_evaluator.go +++ b/pkg/yqlib/stream_evaluator.go @@ -76,6 +76,7 @@ func (s *streamEvaluator) EvaluateFiles(expression string, filenames []string, p } func (s *streamEvaluator) Evaluate(filename string, reader io.Reader, node *ExpressionNode, printer Printer, decoder Decoder) (uint, error) { + filename = resolveFilename(filename) var currentIndex uint err := decoder.Init(reader) diff --git a/pkg/yqlib/utils.go b/pkg/yqlib/utils.go index 4424582a..2843e42d 100644 --- a/pkg/yqlib/utils.go +++ b/pkg/yqlib/utils.go @@ -9,6 +9,29 @@ import ( "os" ) +// filenameAliases maps real file paths to display names. +// Used by front matter handling to preserve original filenames +// when the actual content is read from temporary files. +var filenameAliases = map[string]string{} + +// SetFilenameAlias registers a display name for a file path so that +// the filename operator returns the original name instead of a temp path. +func SetFilenameAlias(realPath string, displayName string) { + filenameAliases[realPath] = displayName +} + +// ClearFilenameAliases removes all filename aliases. +func ClearFilenameAliases() { + filenameAliases = map[string]string{} +} + +func resolveFilename(filename string) string { + if alias, ok := filenameAliases[filename]; ok { + return alias + } + return filename +} + func readStream(filename string) (io.Reader, error) { var reader *bufio.Reader if filename == "-" { @@ -36,6 +59,7 @@ func ReadDocuments(reader io.Reader, decoder Decoder) (*list.List, error) { } func readDocuments(reader io.Reader, filename string, fileIndex int, decoder Decoder) (*list.List, error) { + filename = resolveFilename(filename) err := decoder.Init(reader) if err != nil { return nil, err