| id | 185 | |
|---|---|---|
| title | Expose extended-syntax parsers and the flavor model in pkg/markdown | |
| status | ✅ | |
| summary | Promote every custom goldmark parser (the five extensions) and the flavor support model into a public pkg/markdown/flavor sub-package, take detection off internal/lint, and retire the last two hand-rolled goldmark configs. | |
| model | ||
| depends-on |
|
Give external Go callers one public surface for mdsmith's extended-syntax parsing. Move every custom parser and the feature detection into it. Stop duplicating the goldmark config and the flavor-support table outside pkg/markdown.
Plan 163
scoped pkg/markdown to
CommonMark plus the <?…?> block. It
left extended syntax out. The
library doc
still says "CommonMark only". This plan
revises that scope. The doc edits are in
scope here, not a follow-up.
Three goldmark configs exist today. The
architecture hub
rejects a local goldmark.New() outside
pkg/markdown. That is
the drift 163 removed from
internal/release:
- pkg/markdown
NewParser(): the canonical CommonMark and PI config. - internal/rules/markdownflavor
parser.go: agoldmark.New()with ten extensions and the PI block. Detection only. - internal/schema
validate_content.go: a hand-rolledgoldmark.New()with the table extension and the PI block.
An exhaustive scan finds every custom
goldmark parser. The markers are
ast.NewNodeKind, an Extend method,
and an ASTTransformer. Only two owners
match. The PI block parser already lives
in pkg/markdown; 163
moved it. The five extensions under
pkg/markdown/flavor/ext
are the rest, and no others exist.
Moving those five empties the tree of
custom parsers outside the public
package.
Boundary facts shape the design:
- The five extensions under
pkg/markdown/flavor/ext
import only goldmark. They have no
internal/dependency. - The
Featureenum and the flavor-to-featuresupporttable are pure data. They key onconvention.Flavor. detect.gotakes*lint.File. That couples detection to the linter core.- internal/convention
owns the
Flavoridentity. It is a leaf data package. - pkg/markdown imports
no
internal/package. So theFlavorandFeaturetypes must move down. The rule and internal/convention then depend inward. The dependency never points back.
Behavior must not change. MDS034
diagnostics and --fix output stay
byte-identical.
internal/schema
validation stays byte-identical too.
That is the guarantee 163 held for
sync-docs.
- Decide the package shape. Add a
sub-package
pkg/markdown/flavor. It holds the extensions, theFeaturemodel, the support table, andDetect. It depends on pkg/markdown. Keep it out of the byte-stable core: the core answers "parse and produce"; the new package answers "which features does this document use, and which flavors accept them". Record the rejected flat layout. - Move every custom goldmark parser
into
pkg/markdown/flavor. That is all five extensions: superscript, subscript, math block, inline math, and abbreviation. The abbreviation extension also registers anASTTransformer; move it whole. Move their unit tests from pkg/markdown/flavor/ext too. They import only goldmark, so no dependency direction changes. The PI parser already sits in pkg/markdown from 163, so this leaves no custom parser outside the public package. - Move the
Flavoridentity out of internal/convention. Move theFeatureandsupportmodel out of the rule. Put both inpkg/markdown/flavor. Make internal/convention alias them so internal/config is unchanged. The dependency points convention to flavor only. - Build the public detector
Detect(doc *markdown.Document, accept func(Feature) bool) []Finding. Define the publicFindingandHeadingIDExtrashapes. Take a parsed document, not*lint.File. This keeps pkg/markdown free of any internal/lint import. - Reduce
internal/rules/markdownflavor
to a rule adapter. It maps config and
convention in. It maps
flavor.Findingto diagnostics and fixes out. Therule.Ruleandrule.FixableRulecontract does not change. - Migrate
internal/schema
validate_content.goand the rule'sparser.goonto the public constructor. Leave nogoldmark.New(underinternal/. - Add a contract test that locks the
pkg/markdown/flavorAPI shape. A new external surface ships with a contract test. - Update the docs this scope
contradicts. Fix the
library doc
"CommonMark only" text and its
stable-surface list. Fix the
cross-system
versioning bullet. Fix the
architecture hub
note. Run
mdsmith fixto refresh catalogs.
-
pkg/markdown/flavorexposes the five extension constructors, theFlavorandFeaturemodel, andDetect. The compatibility policy documents it. Verifies ISP and OCP. - No package under
pkg/markdown imports
internal/. The checkgrep -r mdsmith/internal pkg/markdownis empty. Verifies DIP. - One goldmark config remains, under
pkg/markdown. No
goldmark.New(exists underinternal/. - No
ast.NewNodeKind, goldmarkExtendmethod, or customparser.BlockParser,parser.InlineParser, orASTTransformerexists underinternal/orcmd/. Every custom parser lives in pkg/markdown. Verifies SRP and DIP. - internal/convention
and the rule define no own
FlavororFeaturetype. Both depend inward. Verifies DIP and SRP. - MDS034 diagnostics,
--fixoutput, and internal/schema validation are byte-identical before and after. Table tests pin this. Verifies Liskov. - Every moved or new function ships
its dedicated unit test. A contract
test locks the
pkg/markdown/flavorshape. Verifies the test pyramid. - The library doc, cross-system, and architecture hub no longer say "CommonMark only". The boundary and versioning entries cover the flavor surface.
- All tests pass:
go test ./.... -
go tool golangci-lint runreports no issues. -
mdsmith check .passes. The coverage gate holds.