Skip to content

Commit 37d81b0

Browse files
Make manifests authoritative for documentation
1 parent a606b81 commit 37d81b0

4 files changed

Lines changed: 127 additions & 68 deletions

File tree

‎docgen/README.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ artifact. Pursuit, search, and static rendering consume it; they do not define
3232
it. This is why `docgen` is a top-level workspace package rather than part of a
3333
Pursuit application or the public `registry-lib` package.
3434

35+
Package metadata always comes from the canonical `purs.json` stored in the
36+
registry tarball. Historical Pursuit JSON is used during migration only for
37+
compiler-produced documentation and resolution data; it is not an alternative
38+
source for package identity, metadata, dependencies, or source files.
39+
3540
The main modules are:
3641

3742
- `Registry.Docgen.Docs`: the canonical in-memory documentation model.

‎docgen/src/Registry/Docgen/Convert.purs‎

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,43 +15,51 @@ import Data.Maybe (Maybe(..), fromMaybe)
1515
import Data.Newtype (unwrap)
1616
import Data.String as String
1717
import Data.Traversable (traverse)
18-
import Registry.Docgen.Docs (DataConstructorName(..), DocChildDeclaration(..), DocChildDeclarationInfo(..), DocConstraint(..), DocDeclaration(..), DocDeclarationInfo(..), DocModule(..), DocPackage(..), DocReexport(..), DocType(..), ForallBinding(..), FunDep(..), Ident(..), InfixAlias(..), IntLiteral(..), ModuleName(..), OperatorName(..), Qualified(..), RawRange, Readme, RowLabel(..), RowRep, SourceArtifact, SourceSpan(..), StringLiteral, TypeName(..), TypeVar(..), ValueName(..), isPrim, schemaVersion)
18+
import Registry.Docgen.Docs (DataConstructorName(..), DocChildDeclaration(..), DocChildDeclarationInfo(..), DocConstraint(..), DocDeclaration(..), DocDeclarationInfo(..), DocModule(..), DocPackage(..), DocReexport(..), DocType(..), ForallBinding(..), FunDep(..), Ident(..), InfixAlias(..), IntLiteral(..), ModuleName(..), OperatorName(..), Qualified(..), RawRange(..), Readme, RowLabel(..), RowRep, SourceArtifact, SourceSpan(..), StringLiteral, TypeName(..), TypeVar(..), ValueName(..), isPrim, schemaVersion)
1919
import Registry.Docgen.Legacy.Docs (InPackage(..))
2020
import Registry.Docgen.Legacy.Docs as L
21-
import Registry.Location (Location(..))
22-
import Registry.PackageName (PackageName)
21+
import Registry.LimitedString as LimitedString
22+
import Registry.Manifest (Manifest(..))
23+
import Registry.PackageName as PackageName
24+
import Registry.Range as Range
2325
import Registry.Version as Version
2426
import Safe.Coerce (coerce)
2527

26-
-- | Convert historical Pursuit documentation while taking declared dependency
27-
-- | ranges from the authoritative registry manifest.
28+
-- | Convert historical Pursuit documentation while taking all package metadata
29+
-- | from the authoritative registry manifest.
2830
fromLegacyPackage
29-
:: { dependencies :: Map PackageName RawRange
31+
:: { manifest :: Manifest
3032
, readme :: Maybe Readme
3133
, sourceArtifact :: SourceArtifact
3234
, sourcePaths :: Map ModuleName String
3335
}
3436
-> L.DocPackage
3537
-> Either String DocPackage
36-
fromLegacyPackage input (L.DocPackage pkg@{ github: L.GithubData github, packageMeta: L.DocPackageMeta meta }) = do
37-
compilerVersion <- Version.parse pkg.compilerVersion
38-
modules <- traverse convertModule pkg.modules
39-
pure $ DocPackage
40-
{ schemaVersion
41-
, compilerVersion
42-
, sourceArtifact: input.sourceArtifact
43-
, dependencies: input.dependencies
44-
, description: meta.description
45-
, license: meta.license
46-
, location
47-
, locationRef: Just pkg.versionTag
48-
, name: coerce meta.name
49-
, modules
50-
, readme: input.readme
51-
, resolvedDependencies: pkg.resolvedDependencies
52-
, resolvedModulePackages: pkg.moduleMap
53-
, version: pkg.version
54-
}
38+
fromLegacyPackage input (L.DocPackage pkg@{ packageMeta: L.DocPackageMeta meta }) = do
39+
let Manifest manifest = input.manifest
40+
if meta.name /= manifest.name then
41+
Left $ "Legacy documentation package " <> PackageName.print meta.name <> " does not match manifest package " <> PackageName.print manifest.name
42+
else if pkg.version /= manifest.version then
43+
Left $ "Legacy documentation version " <> Version.print pkg.version <> " does not match manifest version " <> Version.print manifest.version
44+
else do
45+
compilerVersion <- Version.parse pkg.compilerVersion
46+
modules <- traverse convertModule pkg.modules
47+
pure $ DocPackage
48+
{ schemaVersion
49+
, compilerVersion
50+
, sourceArtifact: input.sourceArtifact
51+
, dependencies: map (RawRange <<< Range.print) manifest.dependencies
52+
, description: map LimitedString.print manifest.description
53+
, license: manifest.license
54+
, location: manifest.location
55+
, locationRef: Just manifest.ref
56+
, name: manifest.name
57+
, modules
58+
, readme: input.readme
59+
, resolvedDependencies: pkg.resolvedDependencies
60+
, resolvedModulePackages: pkg.moduleMap
61+
, version: manifest.version
62+
}
5563
where
5664
convertModule legacy@(L.DocModule { name })
5765
| isPrim name = Right $ fromLegacyModule sourcePaths legacy
@@ -61,13 +69,6 @@ fromLegacyPackage input (L.DocPackage pkg@{ github: L.GithubData github, package
6169

6270
sourcePaths = input.sourcePaths
6371

64-
location :: Location
65-
location = GitHub
66-
{ owner: github.user
67-
, repo: github.repo
68-
, subdir: Nothing
69-
}
70-
7172
fromLegacyModule :: Map ModuleName String -> L.DocModule -> DocModule
7273
fromLegacyModule sourcePaths (L.DocModule mod@{ name: ownerModule }) =
7374
DocModule

‎docgen/src/Registry/Docgen/Generate.purs‎

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,15 @@ import Data.String as String
2222
import Data.Tuple (Tuple(..))
2323
import PureScript.CST.Types as CST
2424
import Registry.Docgen.Convert as Convert
25-
import Registry.Docgen.Docs (DocModule(..), DocPackage(..), ModuleName, RawRange, Readme, SourceArtifact, schemaVersion)
25+
import Registry.Docgen.Docs (DocModule(..), DocPackage(..), ModuleName, RawRange(..), Readme, SourceArtifact, schemaVersion)
2626
import Registry.Docgen.Legacy.Docs as Legacy
2727
import Registry.Docgen.Reexports (ReexportError)
2828
import Registry.Docgen.Reexports as Reexports
29-
import Registry.License (License)
30-
import Registry.Location (Location)
29+
import Registry.LimitedString as LimitedString
30+
import Registry.Manifest (Manifest(..))
3131
import Registry.PackageName (PackageName)
3232
import Registry.PackageName as PackageName
33+
import Registry.Range as Range
3334
import Registry.Version (Version)
3435
import Safe.Coerce (coerce)
3536

@@ -42,17 +43,11 @@ type ModuleInput =
4243

4344
type PackageInput =
4445
{ compilerVersion :: Version
45-
, dependencies :: Map PackageName RawRange
46-
, description :: Maybe String
47-
, license :: License
48-
, location :: Location
49-
, locationRef :: Maybe String
46+
, manifest :: Manifest
5047
, modules :: Array ModuleInput
51-
, name :: PackageName
5248
, readme :: Maybe Readme
5349
, resolvedDependencies :: Map PackageName Version
5450
, sourceArtifact :: SourceArtifact
55-
, version :: Version
5651
}
5752

5853
data GenerationError
@@ -79,12 +74,12 @@ printGenerationError = case _ of
7974
Reexports.printReexportError error
8075

8176
generatePackage :: PackageInput -> Either GenerationError DocPackage
82-
generatePackage input = do
77+
generatePackage input@{ manifest: Manifest manifest } = do
8378
let sortedInputs = Array.sortBy (comparing docsModuleName) input.modules
8479
_ <- foldM validateModule Set.empty sortedInputs
85-
let packageModules = Array.filter (_.package >>> eq input.name) sortedInputs
80+
let packageModules = Array.filter (_.package >>> eq manifest.name) sortedInputs
8681
if Array.null packageModules then
87-
Left $ NoPackageModules input.name
82+
Left $ NoPackageModules manifest.name
8883
else do
8984
let sourcePaths = Map.fromFoldable $ map (\moduleInput -> Tuple (docsModuleName moduleInput) moduleInput.sourcePath) sortedInputs
9085
let converted = map (Convert.fromLegacyModule sourcePaths <<< _.docs) sortedInputs
@@ -94,17 +89,17 @@ generatePackage input = do
9489
{ schemaVersion
9590
, compilerVersion: input.compilerVersion
9691
, sourceArtifact: input.sourceArtifact
97-
, dependencies: input.dependencies
98-
, description: input.description
99-
, license: input.license
100-
, location: input.location
101-
, locationRef: input.locationRef
102-
, modules: Array.filter (\(DocModule { name }) -> Map.lookup name modulePackages == Just input.name) resolved
103-
, name: input.name
92+
, dependencies: map (RawRange <<< Range.print) manifest.dependencies
93+
, description: map LimitedString.print manifest.description
94+
, license: manifest.license
95+
, location: manifest.location
96+
, locationRef: Just manifest.ref
97+
, modules: Array.filter (\(DocModule { name }) -> Map.lookup name modulePackages == Just manifest.name) resolved
98+
, name: manifest.name
10499
, readme: input.readme
105100
, resolvedDependencies: input.resolvedDependencies
106101
, resolvedModulePackages: modulePackages
107-
, version: input.version
102+
, version: manifest.version
108103
}
109104
where
110105
validateModule seen moduleInput = do
@@ -116,7 +111,7 @@ generatePackage input = do
116111
Left $ DuplicateModule docsName
117112
else if not (isPackageRelativePath moduleInput.sourcePath) then
118113
Left $ InvalidSourcePath docsName moduleInput.sourcePath
119-
else if moduleInput.package /= input.name && not (Map.member moduleInput.package input.resolvedDependencies) then
114+
else if moduleInput.package /= manifest.name && not (Map.member moduleInput.package input.resolvedDependencies) then
120115
Left $ UnknownModulePackage docsName moduleInput.package
121116
else
122117
Right $ Set.insert docsName seen

‎docgen/test/Test/Registry/Docgen.purs‎

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@ import Registry.Docgen.Legacy.JSON as Legacy.JSON
3131
import Registry.Docgen.Package.Render (defaultPackageLinker, htmlCodeRenderer, renderDeclarationInfo, renderDocument, renderModule)
3232
import Registry.Docgen.Reexports (ReexportError(..), modulesWithReexports, printReexportError)
3333
import Registry.License as License
34+
import Registry.LimitedString as LimitedString
3435
import Registry.Location (Location(..))
36+
import Registry.Manifest (Manifest(..))
3537
import Registry.PackageName (PackageName)
3638
import Registry.PackageName as PackageName
39+
import Registry.Range (Range)
40+
import Registry.Range as Range
3741
import Registry.Sha256 as Sha256
3842
import Registry.Test.Assert as Assert
3943
import Registry.Test.Utils as Utils
@@ -82,8 +86,8 @@ main = runSpecAndExitProcess [ consoleReporter ] do
8286
let readme = Readme { content: "# Undefined", extension: Just "md" }
8387
let
8488
conversionInput =
85-
{ dependencies: Map.empty
86-
, readme: Just readme
89+
{ readme: Just readme
90+
, manifest: undefinedManifest
8791
, sourceArtifact: sourceArtifactFixture
8892
, sourcePaths: Map.singleton (ModuleName "Undefined") "custom/Undefined.purs"
8993
}
@@ -99,10 +103,33 @@ main = runSpecAndExitProcess [ consoleReporter ] do
99103
span.path `Assert.shouldEqual` "custom/Undefined.purs"
100104
_ ->
101105
Assert.fail "Expected one converted declaration with a source span"
102-
let manifestDependencies = Map.singleton (packageName "prelude") (RawRange ">=4.0.0 <5.0.0")
103-
case Convert.fromLegacyPackage (conversionInput { dependencies = manifestDependencies }) legacy of
104-
Right (DocPackage { dependencies }) -> dependencies `Assert.shouldEqual` manifestDependencies
105-
Left err -> Assert.fail $ "Failed to convert historical fixture with manifest dependencies: " <> err
106+
let Manifest manifest = undefinedManifest
107+
let canonicalDescription = Utils.fromRight "description" $ LimitedString.parse "Canonical package metadata"
108+
let manifestDependencies = Map.singleton (packageName "prelude") (range ">=4.0.0 <5.0.0")
109+
let
110+
authoritativeManifest = Manifest $ manifest
111+
{ dependencies = manifestDependencies
112+
, description = Just canonicalDescription
113+
, license = Utils.fromRight "license" $ License.parse "BSD-3-Clause"
114+
, location = Git { url: "https://example.com/undefined.git", subdir: Nothing }
115+
, ref = "release-1.0.2"
116+
}
117+
case Convert.fromLegacyPackage (conversionInput { manifest = authoritativeManifest }) legacy of
118+
Right (DocPackage convertedPackage) -> do
119+
convertedPackage.dependencies `Assert.shouldEqual` Map.singleton (packageName "prelude") (RawRange ">=4.0.0 <5.0.0")
120+
convertedPackage.description `Assert.shouldEqual` Just "Canonical package metadata"
121+
convertedPackage.license `Assert.shouldEqual` (Utils.fromRight "license" $ License.parse "BSD-3-Clause")
122+
convertedPackage.location `Assert.shouldEqual` Git { url: "https://example.com/undefined.git", subdir: Nothing }
123+
convertedPackage.locationRef `Assert.shouldEqual` Just "release-1.0.2"
124+
Left err -> Assert.fail $ "Failed to convert historical fixture with authoritative manifest: " <> err
125+
let mismatchedManifest = Manifest $ manifest { name = packageName "other" }
126+
case Convert.fromLegacyPackage (conversionInput { manifest = mismatchedManifest }) legacy of
127+
Left err -> shouldContainString err "does not match manifest package other"
128+
Right _ -> Assert.fail "Conversion unexpectedly accepted a mismatched manifest"
129+
let mismatchedVersion = Manifest $ manifest { version = version "2.0.0" }
130+
case Convert.fromLegacyPackage (conversionInput { manifest = mismatchedVersion }) legacy of
131+
Left err -> shouldContainString err "does not match manifest version 2.0.0"
132+
Right _ -> Assert.fail "Conversion unexpectedly accepted a mismatched manifest version"
106133
case Convert.fromLegacyPackage (conversionInput { sourcePaths = Map.empty }) legacy of
107134
Left err -> shouldContainString err "Missing package-relative source path for module Undefined"
108135
Right _ -> Assert.fail "Conversion unexpectedly accepted a missing module source path"
@@ -128,7 +155,13 @@ main = runSpecAndExitProcess [ consoleReporter ] do
128155
let reversed = Utils.fromRight "Reversed generation failed" $ Generate.generatePackage generationInput { modules = Array.reverse generationInput.modules }
129156
Codec.encode Docgen.Codec.docPackage generated `Assert.shouldEqual` Codec.encode Docgen.Codec.docPackage reversed
130157
case generated of
131-
DocPackage { modules: [ DocModule { name: ModuleName "A", reexports: [ DocReexport { moduleName: ModuleName "B", declarations: [ DocDeclaration { sourceSpan: Just (SourceSpan { path: "src/B.purs" }) } ] } ] } ], resolvedModulePackages } -> do
158+
DocPackage { dependencies, description, location, locationRef, modules: [ DocModule { name: ModuleName "A", reexports: [ DocReexport { moduleName: ModuleName "B", declarations: [ DocDeclaration { sourceSpan: Just (SourceSpan { path: "src/B.purs" }) } ] } ] } ], name, resolvedModulePackages, version: generatedVersion } -> do
159+
dependencies `Assert.shouldEqual` Map.singleton dependency (RawRange ">=2.0.0 <3.0.0")
160+
description `Assert.shouldEqual` Just "Example"
161+
location `Assert.shouldEqual` Git { url: "https://example.com/repo.git", subdir: Just "packages/example" }
162+
locationRef `Assert.shouldEqual` Just "v1.0.0"
163+
name `Assert.shouldEqual` packageName "example"
164+
generatedVersion `Assert.shouldEqual` version "1.0.0"
132165
Map.lookup (ModuleName "A") resolvedModulePackages `Assert.shouldEqual` Just (packageName "example")
133166
Map.lookup (ModuleName "B") resolvedModulePackages `Assert.shouldEqual` Just dependency
134167
_ -> Assert.fail "Generated package did not preserve deterministic ownership, reexports, and source paths"
@@ -138,7 +171,7 @@ main = runSpecAndExitProcess [ consoleReporter ] do
138171
case Generate.generatePackage duplicate of
139172
Left (DuplicateModule (ModuleName "B")) -> pure unit
140173
_ -> Assert.fail "Generation unexpectedly accepted a duplicate module"
141-
let absolute = generationInput { modules = map (\moduleInput -> if moduleInput.package == generationInput.name then moduleInput { sourcePath = "/tmp/A.purs" } else moduleInput) generationInput.modules }
174+
let absolute = generationInput { modules = map (\moduleInput -> if moduleInput.package == packageName "example" then moduleInput { sourcePath = "/tmp/A.purs" } else moduleInput) generationInput.modules }
142175
case Generate.generatePackage absolute of
143176
Left (InvalidSourcePath (ModuleName "A") "/tmp/A.purs") -> pure unit
144177
_ -> Assert.fail "Generation unexpectedly accepted an absolute source path"
@@ -438,11 +471,7 @@ legacyValue title signature span = L.Declaration
438471
generationInput :: Generate.PackageInput
439472
generationInput =
440473
{ compilerVersion: version "0.15.15"
441-
, dependencies: Map.singleton dependency (RawRange ">=2.0.0 <3.0.0")
442-
, description: Just "Example"
443-
, license: Utils.fromRight "license" $ License.parse "BSD-3-Clause"
444-
, location: Git { url: "https://example.com/repo.git", subdir: Just "packages/example" }
445-
, locationRef: Nothing
474+
, manifest: generationManifest
446475
, modules:
447476
[ { docs: legacyModuleWithDeclaration "B" (legacyValue "value" (L.TypeVar "a") (Just sourceSpan))
448477
, package: dependency
@@ -455,13 +484,39 @@ generationInput =
455484
, sourcePath: "custom/A.purs"
456485
}
457486
]
458-
, name: packageName "example"
459487
, readme: Just $ Readme { content: "# Example", extension: Just "md" }
460488
, resolvedDependencies: Map.singleton dependency (version "2.3.4")
461489
, sourceArtifact: sourceArtifactFixture
490+
}
491+
492+
generationManifest :: Manifest
493+
generationManifest = Manifest
494+
{ dependencies: Map.singleton dependency (range ">=2.0.0 <3.0.0")
495+
, description: Just $ Utils.fromRight "description" $ LimitedString.parse "Example"
496+
, excludeFiles: Nothing
497+
, includeFiles: Nothing
498+
, license: Utils.fromRight "license" $ License.parse "BSD-3-Clause"
499+
, location: Git { url: "https://example.com/repo.git", subdir: Just "packages/example" }
500+
, name: packageName "example"
501+
, owners: Nothing
502+
, ref: "v1.0.0"
462503
, version: version "1.0.0"
463504
}
464505

506+
undefinedManifest :: Manifest
507+
undefinedManifest = Manifest
508+
{ dependencies: Map.empty
509+
, description: Just $ Utils.fromRight "description" $ LimitedString.parse "Package containing the undefined value."
510+
, excludeFiles: Nothing
511+
, includeFiles: Nothing
512+
, license: Utils.fromRight "license" $ License.parse "MIT"
513+
, location: GitHub { owner: "bklaric", repo: "purescript-undefined", subdir: Nothing }
514+
, name: packageName "undefined"
515+
, owners: Nothing
516+
, ref: "v1.0.2"
517+
, version: version "1.0.2"
518+
}
519+
465520
package :: DocPackage
466521
package = DocPackage
467522
{ schemaVersion
@@ -489,6 +544,9 @@ packageName = Utils.fromRight "package name" <<< PackageName.parse
489544
version :: String -> Version
490545
version = Utils.fromRight "version" <<< Version.parse
491546

547+
range :: String -> Range
548+
range = Utils.fromRight "range" <<< Range.parse
549+
492550
sourceArtifactFixture :: SourceArtifact
493551
sourceArtifactFixture = SourceArtifact
494552
{ bytes: 42.0

0 commit comments

Comments
 (0)