diff --git a/README.md b/README.md index 8c21793..0477307 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,12 @@ if (boundaryLocation is not null) ## Custom Merge Strategy +### Merge identity + +If your merge strategy inlines/expands directives (e.g., `@import`, `#include`), use the authoritative dependency +identity returned by your `IResourceResolver`. TinyPreprocessor provides this mapping to merge via +`MergeContext.ResolvedReferences`. + Implement `IMergeStrategy` for custom output formatting: ```csharp @@ -237,6 +243,11 @@ public sealed class JsonMergeStrategy : IMergeStrategy, Inc // mergeContext.SourceMapBuilder.AddOffsetSegment(resourceId, generatedStartOffset, originalStartOffset, length) // Use mergeContext.Diagnostics to report issues + // If you need to inline/expand directives: + // - Use mergeContext.ResolvedReferences to map (requestingResourceId, directiveIndex) -> resolved dependency ResourceId + // - Then fetch the already-processed dependency content from mergeContext.ResolvedCache + // Do NOT re-derive ResourceIds from raw reference strings (resolvers may implement custom mapping). + return ReadOnlyMemory.Empty; } } diff --git a/docs/02-diagnostics-system.md b/docs/02-diagnostics-system.md index 276a84a..22c4d59 100644 --- a/docs/02-diagnostics-system.md +++ b/docs/02-diagnostics-system.md @@ -161,29 +161,26 @@ diagnostics = new DiagnosticCollection() result = await resolver.ResolveAsync(reference, currentResource, ct) if not result.IsSuccess: diagnostics.Add(result.Error) - -// During merge -// Merge strategies should only add diagnostics for merge-time concerns (e.g., directive placement rules, -// unsupported constructs, or missing data needed to build output), not for reference resolution. - // Continue processing other directives +// During cycle detection +for each cycle in graph.DetectCycles(): + diagnostics.Add(new CircularDependencyDiagnostic(cycle)) +``` ### Resolution vs. Merge Responsibility TinyPreprocessor separates responsibilities between phases: - **Resolution diagnostics** (e.g., `TPP0100` `ResolutionFailedDiagnostic`) are emitted when - `IResourceResolver.ResolveAsync(reference, relativeTo)` cannot produce an `IResource`. + `IResourceResolver.ResolveAsync(reference, relativeTo)` cannot produce an `IResource`. - **Merge diagnostics** (e.g., `TPP0300` `NonWholeLineDirectiveDiagnostic`) are emitted when merge logic detects - issues while producing output. + issues while producing output. -Merge strategies that inline/expand directives must use the authoritative resolved dependency identity provided via -`MergeContext.ResolvedReferences`. They must not re-derive a `ResourceId` from the raw reference string using -path heuristics, because resolvers may implement custom mapping. +Merge strategies should only add diagnostics for merge-time concerns (e.g., directive placement rules, unsupported +constructs, or missing data needed to build output), not for reference resolution. -// During cycle detection -for each cycle in graph.DetectCycles(): - diagnostics.Add(new CircularDependencyDiagnostic(cycle)) -``` +Merge strategies that inline/expand directives must use the authoritative resolved dependency identity provided via +`MergeContext.ResolvedReferences`. They must not re-derive a `ResourceId` from the raw reference string using path +heuristics, because resolvers may implement custom mapping. ### Checking Results diff --git a/docs/05-merge-system.md b/docs/05-merge-system.md index ee4e496..761965d 100644 --- a/docs/05-merge-system.md +++ b/docs/05-merge-system.md @@ -37,7 +37,7 @@ class MergeContext SourceMapBuilder : SourceMapBuilder // for recording mappings Diagnostics : DiagnosticCollection // for reporting issues ResolvedCache : IReadOnlyDictionary> // for cross-referencing - ResolvedReferences : IReadOnlyDictionary // directive occurrence -> resolved dependency id + ResolvedReferences : IReadOnlyDictionary // directive occurrence -> resolved dependency id DirectiveModel : IDirectiveModel // for interpreting directive locations ContentModel : IContentModel // for interpreting offsets + slicing content ``` @@ -62,6 +62,8 @@ dependency content. For these strategies, the **only** correct way to identify a - `directiveIndex` is the zero-based index of the directive in that resource's parsed directive list - **Value**: the resolved target `ResourceId` returned by the resolver +In code, the key type is `MergeContext.ResolvedReferenceKey`. + If a directive failed to resolve, it has **no entry** in this map. --- diff --git a/docs/06-preprocessor-orchestrator.md b/docs/06-preprocessor-orchestrator.md index 22bd6c4..5d8448f 100644 --- a/docs/06-preprocessor-orchestrator.md +++ b/docs/06-preprocessor-orchestrator.md @@ -81,7 +81,7 @@ class Preprocessor graph ← new ResourceDependencyGraph() cache ← new Dictionary() sourceMapBuilder ← new SourceMapBuilder() - resolvedReferences ← new Dictionary<(ResourceId, directiveIndex), ResourceId>() + resolvedReferences ← new Dictionary() // Phase 1: Recursive resolution await ResolveRecursiveAsync(root, depth: 0, ...)