@@ -18,28 +18,41 @@ if (!fs.existsSync(readmePath)) die("README.md not found");
1818
1919const readme = fs . readFileSync ( readmePath , "utf8" ) ;
2020
21- // Find the Mermaid block inside the "## Architecture" section.
22- function extractArchitectureMermaid ( md ) {
23- const sectionRe = / ( ^ # # \s + A r c h i t e c t u r e \s * $ ) ( [ \s \S ] * ?) (? = ^ \s * # # \s + | \s * $ ) / m;
24- const sectionMatch = md . match ( sectionRe ) ;
25- if ( ! sectionMatch ) return null ;
26-
27- const sectionBody = sectionMatch [ 2 ] ;
28-
29- const mermaidRe = / ` ` ` m e r m a i d \s * \n ( [ \s \S ] * ?) \n ` ` ` / m;
30- const mermaidMatch = sectionBody . match ( mermaidRe ) ;
31- if ( ! mermaidMatch ) return null ;
32-
33- return {
34- sectionHeader : sectionMatch [ 1 ] ,
35- sectionBody,
36- mermaidBody : mermaidMatch [ 1 ] . trimEnd ( ) ,
37- } ;
21+ // CRLF-safe helpers
22+ const NL = String . raw `\r?\n` ;
23+
24+ function getArchitectureSection ( md ) {
25+ // Capture "## Architecture" section until next "## " heading or EOF
26+ const sectionRe = new RegExp (
27+ String . raw `(^##\s+Architecture\s*$)([\s\S]*?)(?=^\s*##\s+|\s*$)` ,
28+ "m"
29+ ) ;
30+ const m = md . match ( sectionRe ) ;
31+ if ( ! m ) return null ;
32+ return { header : m [ 1 ] , body : m [ 2 ] , fullMatch : m [ 0 ] } ;
3833}
3934
40- const arch = extractArchitectureMermaid ( readme ) ;
35+ function getFirstMermaidBlock ( text ) {
36+ // CRLF-safe fenced block capture
37+ const mermaidRe = new RegExp (
38+ String . raw "`` `mermaid\\s*" + NL + String.raw"([\\s\\S]*?)" + NL + String.raw"` `` ",
39+ "m"
40+ ) ;
41+ const m = text . match ( mermaidRe ) ;
42+ if ( ! m ) return null ;
43+ return m [ 1 ] . trimEnd ( ) ;
44+ }
45+
46+ const arch = getArchitectureSection ( readme ) ;
4147
4248if ( ! arch ) {
49+ console . log ( 'No "## Architecture" section found in README.md. Nothing to do.' ) ;
50+ process . exit ( 0 ) ;
51+ }
52+
53+ const mermaidBody = getFirstMermaidBlock ( arch . body ) ;
54+
55+ if ( ! mermaidBody ) {
4356 console . log ( 'No Mermaid block found under "## Architecture". Nothing to do.' ) ;
4457 process . exit ( 0 ) ;
4558}
@@ -50,14 +63,14 @@ fs.mkdirSync(docsDir, { recursive: true });
5063const mmdFile = path . join ( docsDir , "architecture.mmd" ) ;
5164const svgFile = path . join ( docsDir , "architecture.svg" ) ;
5265
53- fs . writeFileSync ( mmdFile , arch . mermaidBody + "\n" , "utf8" ) ;
66+ fs . writeFileSync ( mmdFile , mermaidBody + "\n" , "utf8" ) ;
5467
5568// Render using mermaid-cli (mmdc)
5669run ( "mmdc" , [ "-i" , mmdFile , "-o" , svgFile , "-b" , "transparent" ] ) ;
5770
5871console . log ( "Rendered docs/architecture.svg" ) ;
5972
60- // Raw GitHub URL so PyPI can render the image
73+ // Use raw GitHub URL so PyPI can load the image
6174const imgUrl = "https://raw.githubusercontent.com/marcostfermin/GeoFabric/main/docs/architecture.svg" ;
6275
6376const replacement =
@@ -70,30 +83,33 @@ const replacement =
7083 "<summary>Mermaid source</summary>" ,
7184 "" ,
7285 "```mermaid" ,
73- arch . mermaidBody . trimEnd ( ) ,
86+ mermaidBody . trimEnd ( ) ,
7487 "```" ,
7588 "" ,
7689 "</details>" ,
7790 ""
7891 ] . join ( "\n" ) ;
7992
80- // Rewrite only the Mermaid block inside the Architecture section
81- const sectionRe = / ( ^ # # \s + A r c h i t e c t u r e \s * $ ) ( [ \s \S ] * ?) (? = ^ \s * # # \s + | \s * $ ) / m;
82- const rewritten = readme . replace ( sectionRe , ( full , headerLine , body ) => {
83- const mermaidRe = / ` ` ` m e r m a i d \s * \n [ \s \S ] * ?\n ` ` ` / m;
93+ // Replace the first Mermaid fenced block inside the Architecture section (CRLF-safe)
94+ const mermaidBlockRe = new RegExp (
95+ String . raw "`` `mermaid\\s*" + NL + String.raw"[\\s\\S]*?" + NL + String.raw"` `` ",
96+ "m"
97+ ) ;
98+
99+ const alreadyRendered = arch . body . includes ( imgUrl ) && arch . body . includes ( "<details>" ) && arch . body . includes ( "Mermaid source" ) ;
84100
85- // Idempotency: don't churn commits if already rendered
86- const alreadyHasImg = body . includes ( imgUrl ) ;
87- const alreadyHasDetails = body . includes ( "<details>" ) && body . includes ( "Mermaid source" ) ;
88- if ( alreadyHasImg && alreadyHasDetails ) return full ;
101+ let rewritten = readme ;
89102
90- const newBody = body . replace ( mermaidRe , replacement ) ;
91- return ` ${ headerLine } ${ newBody } ` ;
92- } ) ;
103+ if ( ! alreadyRendered ) {
104+ const newArchBody = arch . body . replace ( mermaidBlockRe , replacement ) ;
105+ rewritten = readme . replace ( arch . fullMatch , ` ${ arch . header } ${ newArchBody } ` ) ;
93106
94- if ( rewritten !== readme ) {
95- fs . writeFileSync ( readmePath , rewritten , "utf8" ) ;
96- console . log ( 'Updated README.md (Architecture section now uses rendered SVG and keeps Mermaid source).' ) ;
107+ if ( rewritten !== readme ) {
108+ fs . writeFileSync ( readmePath , rewritten , "utf8" ) ;
109+ console . log ( "Updated README.md (Architecture now uses rendered SVG + keeps Mermaid source)." ) ;
110+ } else {
111+ console . log ( "README.md not modified (replacement did not apply)." ) ;
112+ }
97113} else {
98- console . log ( "README.md already up to date . No changes made ." ) ;
114+ console . log ( "README.md already contains rendered diagram markup . No rewrite needed ." ) ;
99115}
0 commit comments