Skip to content

Commit d6ec7ff

Browse files
committed
plan 52: tighten schema-source matching to one level deep
isSchemaOrArchetypeFile matched any *.md under or below a configured archetype root. With archetype-roots: ["."] it matched the whole repo, disabling required-structure validation and suppressing the misplaced <?require?> warning for every document. Now the check accepts only files that are direct children of the root: archetype discovery only finds <root>/<name>.md, so the schema-source check should match that exact shape. Files in subdirectories get treated as normal documents again. Added tests for both cases: archetype-roots "." with a nested doc, and a nested file under "archetypes/sub/" with the default root.
1 parent 39493bb commit d6ec7ff

2 files changed

Lines changed: 46 additions & 9 deletions

File tree

internal/rules/requiredstructure/rule.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -253,17 +253,26 @@ func (r *Rule) isSchemaOrArchetypeFile(f *lint.File) bool {
253253
}
254254
for _, root := range roots {
255255
cleanRoot := filepath.ToSlash(filepath.Clean(root))
256-
if cleanRoot == "." {
257-
cleanRoot = ""
258-
} else {
259-
cleanRoot += "/"
260-
}
261256
for _, c := range candidates {
262-
if strings.HasSuffix(c, ".md") &&
263-
(cleanRoot == "" ||
264-
strings.HasPrefix(c, cleanRoot)) {
265-
return true
257+
if !strings.HasSuffix(c, ".md") {
258+
continue
259+
}
260+
// An archetype lives at "<root>/<name>.md" — exactly one
261+
// file deep. `.` is a root with no prefix at all, so the
262+
// candidate must have no path separators.
263+
var rel string
264+
switch {
265+
case cleanRoot == ".":
266+
rel = c
267+
case strings.HasPrefix(c, cleanRoot+"/"):
268+
rel = strings.TrimPrefix(c, cleanRoot+"/")
269+
default:
270+
continue
271+
}
272+
if rel == "" || strings.Contains(rel, "/") {
273+
continue
266274
}
275+
return true
267276
}
268277
}
269278
return false

internal/rules/requiredstructure/rule_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,34 @@ func newFileInRootDirOnly(t *testing.T, root, name, body string) *lint.File {
269269
return f
270270
}
271271

272+
func TestCheck_DotRootOnlyMatchesTopLevelMarkdown(t *testing.T) {
273+
root := t.TempDir()
274+
// Nested doc under a subdirectory — must NOT be treated as an
275+
// archetype source just because archetype-roots is ".".
276+
require.NoError(t, os.MkdirAll(filepath.Join(root, "docs"), 0o755))
277+
src := "<?require\nfilename: \"doc-*.md\"\n?>\n# Title\n"
278+
f := newFileInRoot(t, root, filepath.Join("docs", "doc.md"), src)
279+
r := &Rule{ArchetypeRoots: []string{"."}}
280+
diags := r.Check(f)
281+
// Expect the misplaced-require warning because docs/doc.md is a
282+
// normal doc, not a top-level schema source.
283+
expectDiagMsg(t, diags, "<?require?>")
284+
}
285+
286+
func TestCheck_NonDotRootOnlyMatchesDirectChildren(t *testing.T) {
287+
root := t.TempDir()
288+
// File lives in archetypes/sub/story.md — deeper than archetype
289+
// discovery supports, so not a schema source.
290+
require.NoError(t, os.MkdirAll(
291+
filepath.Join(root, "archetypes", "sub"), 0o755))
292+
src := "<?require\nfilename: \"story-*.md\"\n?>\n# ?\n"
293+
f := newFileInRoot(t, root,
294+
filepath.Join("archetypes", "sub", "story.md"), src)
295+
r := &Rule{}
296+
diags := r.Check(f)
297+
expectDiagMsg(t, diags, "<?require?>")
298+
}
299+
272300
func TestCheck_ArchetypeRootEscapesProjectRoot(t *testing.T) {
273301
root := t.TempDir()
274302
f := newFileInRoot(t, root, "doc.md", "# Title\n")

0 commit comments

Comments
 (0)