support for rss/calendar
This commit is contained in:
		
							
								
								
									
										234
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										234
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -12,6 +12,8 @@ | |||||||
|         "@doc-utils/jsonschema2markdown": "^0.1.1", |         "@doc-utils/jsonschema2markdown": "^0.1.1", | ||||||
|         "@doc-utils/markdown2html": "^0.2.1", |         "@doc-utils/markdown2html": "^0.2.1", | ||||||
|         "glob": "^10.2.3", |         "glob": "^10.2.3", | ||||||
|  |         "ical": "^0.8.0", | ||||||
|  |         "ical-generator": "^4.1.0", | ||||||
|         "luxon": "^3.3.0", |         "luxon": "^3.3.0", | ||||||
|         "mustache": "^4.2.0", |         "mustache": "^4.2.0", | ||||||
|         "xmlbuilder2": "^3.1.1", |         "xmlbuilder2": "^3.1.1", | ||||||
| @@ -21,6 +23,7 @@ | |||||||
|         "docs2website": "bin/docs2website" |         "docs2website": "bin/docs2website" | ||||||
|       }, |       }, | ||||||
|       "devDependencies": { |       "devDependencies": { | ||||||
|  |         "@types/ical": "^0.8.0", | ||||||
|         "@types/jsdom": "^20.0.1", |         "@types/jsdom": "^20.0.1", | ||||||
|         "@types/luxon": "^3.3.0", |         "@types/luxon": "^3.3.0", | ||||||
|         "@types/mustache": "^4.2.2", |         "@types/mustache": "^4.2.2", | ||||||
| @@ -152,6 +155,43 @@ | |||||||
|       "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.4.tgz", |       "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.4.tgz", | ||||||
|       "integrity": "sha512-MHmwBtCb7OCv1DSivz2UNJXPGU/1btAWRKlqJ2saEhVJkpkvqHMMaOpKg0v4sAbDWSQekHGvPVMM8nQ+Jen03Q==" |       "integrity": "sha512-MHmwBtCb7OCv1DSivz2UNJXPGU/1btAWRKlqJ2saEhVJkpkvqHMMaOpKg0v4sAbDWSQekHGvPVMM8nQ+Jen03Q==" | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/@types/ical": { | ||||||
|  |       "version": "0.8.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@types/ical/-/ical-0.8.0.tgz", | ||||||
|  |       "integrity": "sha512-46KAYxAwRuCh+jRgl9k5cTaXJJkB16gWpvMVPuog9UBBb2zXTf9M0MsfWYBQ21JohFSuMZfPTvk6tohqGr1tCg==", | ||||||
|  |       "dev": true, | ||||||
|  |       "dependencies": { | ||||||
|  |         "rrule": "2.6.4" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@types/ical/node_modules/luxon": { | ||||||
|  |       "version": "1.28.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz", | ||||||
|  |       "integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw==", | ||||||
|  |       "dev": true, | ||||||
|  |       "optional": true, | ||||||
|  |       "engines": { | ||||||
|  |         "node": "*" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@types/ical/node_modules/rrule": { | ||||||
|  |       "version": "2.6.4", | ||||||
|  |       "resolved": "https://registry.npmjs.org/rrule/-/rrule-2.6.4.tgz", | ||||||
|  |       "integrity": "sha512-sLdnh4lmjUqq8liFiOUXD5kWp/FcnbDLPwq5YAc/RrN6120XOPb86Ae5zxF7ttBVq8O3LxjjORMEit1baluahA==", | ||||||
|  |       "dev": true, | ||||||
|  |       "dependencies": { | ||||||
|  |         "tslib": "^1.10.0" | ||||||
|  |       }, | ||||||
|  |       "optionalDependencies": { | ||||||
|  |         "luxon": "^1.21.3" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@types/ical/node_modules/tslib": { | ||||||
|  |       "version": "1.14.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", | ||||||
|  |       "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", | ||||||
|  |       "dev": true | ||||||
|  |     }, | ||||||
|     "node_modules/@types/jsdom": { |     "node_modules/@types/jsdom": { | ||||||
|       "version": "20.0.1", |       "version": "20.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", |       "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", | ||||||
| @@ -167,7 +207,7 @@ | |||||||
|       "version": "3.3.0", |       "version": "3.3.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.0.tgz", |       "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.0.tgz", | ||||||
|       "integrity": "sha512-uKRI5QORDnrGFYgcdAVnHvEIvEZ8noTpP/Bg+HeUzZghwinDlIS87DEenV5r1YoOF9G4x600YsUXLWZ19rmTmg==", |       "integrity": "sha512-uKRI5QORDnrGFYgcdAVnHvEIvEZ8noTpP/Bg+HeUzZghwinDlIS87DEenV5r1YoOF9G4x600YsUXLWZ19rmTmg==", | ||||||
|       "dev": true |       "devOptional": true | ||||||
|     }, |     }, | ||||||
|     "node_modules/@types/mustache": { |     "node_modules/@types/mustache": { | ||||||
|       "version": "4.2.2", |       "version": "4.2.2", | ||||||
| @@ -179,7 +219,7 @@ | |||||||
|       "version": "18.16.13", |       "version": "18.16.13", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.13.tgz", |       "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.13.tgz", | ||||||
|       "integrity": "sha512-uZRomboV1vBL61EBXneL4j9/hEn+1Yqa4LQdpGrKmXFyJmVfWc9JV9+yb2AlnOnuaDnb2PDO3hC6/LKmzJxP1A==", |       "integrity": "sha512-uZRomboV1vBL61EBXneL4j9/hEn+1Yqa4LQdpGrKmXFyJmVfWc9JV9+yb2AlnOnuaDnb2PDO3hC6/LKmzJxP1A==", | ||||||
|       "dev": true |       "devOptional": true | ||||||
|     }, |     }, | ||||||
|     "node_modules/@types/prismjs": { |     "node_modules/@types/prismjs": { | ||||||
|       "version": "1.26.0", |       "version": "1.26.0", | ||||||
| @@ -1061,6 +1101,82 @@ | |||||||
|         "node": ">= 6" |         "node": ">= 6" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/ical": { | ||||||
|  |       "version": "0.8.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/ical/-/ical-0.8.0.tgz", | ||||||
|  |       "integrity": "sha512-/viUSb/RGLLnlgm0lWRlPBtVeQguQRErSPYl3ugnUaKUnzQswKqOG3M8/P1v1AB5NJwlHTuvTq1cs4mpeG2rCg==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "rrule": "2.4.1" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/ical-generator": { | ||||||
|  |       "version": "4.1.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/ical-generator/-/ical-generator-4.1.0.tgz", | ||||||
|  |       "integrity": "sha512-5GrFDJ8SAOj8cB9P1uEZIfKrNxSZ1R2eOQfZePL+CtdWh4RwNXWe8b0goajz+Hu37vcipG3RVldoa2j57Y20IA==", | ||||||
|  |       "dependencies": { | ||||||
|  |         "uuid-random": "^1.3.2" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": "^14.8.0 || >=16.0.0" | ||||||
|  |       }, | ||||||
|  |       "peerDependencies": { | ||||||
|  |         "@touch4it/ical-timezones": ">=1.6.0", | ||||||
|  |         "@types/luxon": ">= 1.26.0", | ||||||
|  |         "@types/mocha": ">= 8.2.1", | ||||||
|  |         "@types/node": ">= 15.0.0", | ||||||
|  |         "dayjs": ">= 1.10.0", | ||||||
|  |         "luxon": ">= 1.26.0", | ||||||
|  |         "moment": ">= 2.29.0", | ||||||
|  |         "moment-timezone": ">= 0.5.33", | ||||||
|  |         "rrule": ">= 2.6.8" | ||||||
|  |       }, | ||||||
|  |       "peerDependenciesMeta": { | ||||||
|  |         "@touch4it/ical-timezones": { | ||||||
|  |           "optional": true | ||||||
|  |         }, | ||||||
|  |         "@types/luxon": { | ||||||
|  |           "optional": true | ||||||
|  |         }, | ||||||
|  |         "@types/mocha": { | ||||||
|  |           "optional": true | ||||||
|  |         }, | ||||||
|  |         "@types/node": { | ||||||
|  |           "optional": true | ||||||
|  |         }, | ||||||
|  |         "dayjs": { | ||||||
|  |           "optional": true | ||||||
|  |         }, | ||||||
|  |         "luxon": { | ||||||
|  |           "optional": true | ||||||
|  |         }, | ||||||
|  |         "moment": { | ||||||
|  |           "optional": true | ||||||
|  |         }, | ||||||
|  |         "moment-timezone": { | ||||||
|  |           "optional": true | ||||||
|  |         }, | ||||||
|  |         "rrule": { | ||||||
|  |           "optional": true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/ical/node_modules/luxon": { | ||||||
|  |       "version": "1.28.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz", | ||||||
|  |       "integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw==", | ||||||
|  |       "optional": true, | ||||||
|  |       "engines": { | ||||||
|  |         "node": "*" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/ical/node_modules/rrule": { | ||||||
|  |       "version": "2.4.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/rrule/-/rrule-2.4.1.tgz", | ||||||
|  |       "integrity": "sha512-+NcvhETefswZq13T8nkuEnnQ6YgUeZaqMqVbp+ZiFDPCbp3AVgQIwUvNVDdMNrP05bKZG9ddDULFp0qZZYDrxg==", | ||||||
|  |       "optionalDependencies": { | ||||||
|  |         "luxon": "^1.3.3" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/iconv-lite": { |     "node_modules/iconv-lite": { | ||||||
|       "version": "0.6.3", |       "version": "0.6.3", | ||||||
|       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", |       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", | ||||||
| @@ -1545,6 +1661,16 @@ | |||||||
|       "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", |       "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", | ||||||
|       "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" |       "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/rrule": { | ||||||
|  |       "version": "2.7.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/rrule/-/rrule-2.7.2.tgz", | ||||||
|  |       "integrity": "sha512-NkBsEEB6FIZOZ3T8frvEBOB243dm46SPufpDckY/Ap/YH24V1zLeMmDY8OA10lk452NdrF621+ynDThE7FQU2A==", | ||||||
|  |       "optional": true, | ||||||
|  |       "peer": true, | ||||||
|  |       "dependencies": { | ||||||
|  |         "tslib": "^2.4.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/rw": { |     "node_modules/rw": { | ||||||
|       "version": "1.3.3", |       "version": "1.3.3", | ||||||
|       "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", |       "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", | ||||||
| @@ -1792,6 +1918,13 @@ | |||||||
|         "node": ">=12" |         "node": ">=12" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/tslib": { | ||||||
|  |       "version": "2.5.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz", | ||||||
|  |       "integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==", | ||||||
|  |       "optional": true, | ||||||
|  |       "peer": true | ||||||
|  |     }, | ||||||
|     "node_modules/type-check": { |     "node_modules/type-check": { | ||||||
|       "version": "0.3.2", |       "version": "0.3.2", | ||||||
|       "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", |       "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", | ||||||
| @@ -1841,6 +1974,11 @@ | |||||||
|         "requires-port": "^1.0.0" |         "requires-port": "^1.0.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/uuid-random": { | ||||||
|  |       "version": "1.3.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/uuid-random/-/uuid-random-1.3.2.tgz", | ||||||
|  |       "integrity": "sha512-UOzej0Le/UgkbWEO8flm+0y+G+ljUon1QWTEZOq1rnMAsxo2+SckbiZdKzAHHlVh6gJqI1TjC/xwgR50MuCrBQ==" | ||||||
|  |     }, | ||||||
|     "node_modules/vega": { |     "node_modules/vega": { | ||||||
|       "version": "5.25.0", |       "version": "5.25.0", | ||||||
|       "resolved": "https://registry.npmjs.org/vega/-/vega-5.25.0.tgz", |       "resolved": "https://registry.npmjs.org/vega/-/vega-5.25.0.tgz", | ||||||
| @@ -2637,6 +2775,40 @@ | |||||||
|       "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.4.tgz", |       "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.4.tgz", | ||||||
|       "integrity": "sha512-MHmwBtCb7OCv1DSivz2UNJXPGU/1btAWRKlqJ2saEhVJkpkvqHMMaOpKg0v4sAbDWSQekHGvPVMM8nQ+Jen03Q==" |       "integrity": "sha512-MHmwBtCb7OCv1DSivz2UNJXPGU/1btAWRKlqJ2saEhVJkpkvqHMMaOpKg0v4sAbDWSQekHGvPVMM8nQ+Jen03Q==" | ||||||
|     }, |     }, | ||||||
|  |     "@types/ical": { | ||||||
|  |       "version": "0.8.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@types/ical/-/ical-0.8.0.tgz", | ||||||
|  |       "integrity": "sha512-46KAYxAwRuCh+jRgl9k5cTaXJJkB16gWpvMVPuog9UBBb2zXTf9M0MsfWYBQ21JohFSuMZfPTvk6tohqGr1tCg==", | ||||||
|  |       "dev": true, | ||||||
|  |       "requires": { | ||||||
|  |         "rrule": "2.6.4" | ||||||
|  |       }, | ||||||
|  |       "dependencies": { | ||||||
|  |         "luxon": { | ||||||
|  |           "version": "1.28.1", | ||||||
|  |           "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz", | ||||||
|  |           "integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw==", | ||||||
|  |           "dev": true, | ||||||
|  |           "optional": true | ||||||
|  |         }, | ||||||
|  |         "rrule": { | ||||||
|  |           "version": "2.6.4", | ||||||
|  |           "resolved": "https://registry.npmjs.org/rrule/-/rrule-2.6.4.tgz", | ||||||
|  |           "integrity": "sha512-sLdnh4lmjUqq8liFiOUXD5kWp/FcnbDLPwq5YAc/RrN6120XOPb86Ae5zxF7ttBVq8O3LxjjORMEit1baluahA==", | ||||||
|  |           "dev": true, | ||||||
|  |           "requires": { | ||||||
|  |             "luxon": "^1.21.3", | ||||||
|  |             "tslib": "^1.10.0" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "tslib": { | ||||||
|  |           "version": "1.14.1", | ||||||
|  |           "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", | ||||||
|  |           "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", | ||||||
|  |           "dev": true | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "@types/jsdom": { |     "@types/jsdom": { | ||||||
|       "version": "20.0.1", |       "version": "20.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", |       "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", | ||||||
| @@ -2652,7 +2824,7 @@ | |||||||
|       "version": "3.3.0", |       "version": "3.3.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.0.tgz", |       "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.0.tgz", | ||||||
|       "integrity": "sha512-uKRI5QORDnrGFYgcdAVnHvEIvEZ8noTpP/Bg+HeUzZghwinDlIS87DEenV5r1YoOF9G4x600YsUXLWZ19rmTmg==", |       "integrity": "sha512-uKRI5QORDnrGFYgcdAVnHvEIvEZ8noTpP/Bg+HeUzZghwinDlIS87DEenV5r1YoOF9G4x600YsUXLWZ19rmTmg==", | ||||||
|       "dev": true |       "devOptional": true | ||||||
|     }, |     }, | ||||||
|     "@types/mustache": { |     "@types/mustache": { | ||||||
|       "version": "4.2.2", |       "version": "4.2.2", | ||||||
| @@ -2664,7 +2836,7 @@ | |||||||
|       "version": "18.16.13", |       "version": "18.16.13", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.13.tgz", |       "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.13.tgz", | ||||||
|       "integrity": "sha512-uZRomboV1vBL61EBXneL4j9/hEn+1Yqa4LQdpGrKmXFyJmVfWc9JV9+yb2AlnOnuaDnb2PDO3hC6/LKmzJxP1A==", |       "integrity": "sha512-uZRomboV1vBL61EBXneL4j9/hEn+1Yqa4LQdpGrKmXFyJmVfWc9JV9+yb2AlnOnuaDnb2PDO3hC6/LKmzJxP1A==", | ||||||
|       "dev": true |       "devOptional": true | ||||||
|     }, |     }, | ||||||
|     "@types/prismjs": { |     "@types/prismjs": { | ||||||
|       "version": "1.26.0", |       "version": "1.26.0", | ||||||
| @@ -3307,6 +3479,38 @@ | |||||||
|         "debug": "4" |         "debug": "4" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "ical": { | ||||||
|  |       "version": "0.8.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/ical/-/ical-0.8.0.tgz", | ||||||
|  |       "integrity": "sha512-/viUSb/RGLLnlgm0lWRlPBtVeQguQRErSPYl3ugnUaKUnzQswKqOG3M8/P1v1AB5NJwlHTuvTq1cs4mpeG2rCg==", | ||||||
|  |       "requires": { | ||||||
|  |         "rrule": "2.4.1" | ||||||
|  |       }, | ||||||
|  |       "dependencies": { | ||||||
|  |         "luxon": { | ||||||
|  |           "version": "1.28.1", | ||||||
|  |           "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz", | ||||||
|  |           "integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw==", | ||||||
|  |           "optional": true | ||||||
|  |         }, | ||||||
|  |         "rrule": { | ||||||
|  |           "version": "2.4.1", | ||||||
|  |           "resolved": "https://registry.npmjs.org/rrule/-/rrule-2.4.1.tgz", | ||||||
|  |           "integrity": "sha512-+NcvhETefswZq13T8nkuEnnQ6YgUeZaqMqVbp+ZiFDPCbp3AVgQIwUvNVDdMNrP05bKZG9ddDULFp0qZZYDrxg==", | ||||||
|  |           "requires": { | ||||||
|  |             "luxon": "^1.3.3" | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "ical-generator": { | ||||||
|  |       "version": "4.1.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/ical-generator/-/ical-generator-4.1.0.tgz", | ||||||
|  |       "integrity": "sha512-5GrFDJ8SAOj8cB9P1uEZIfKrNxSZ1R2eOQfZePL+CtdWh4RwNXWe8b0goajz+Hu37vcipG3RVldoa2j57Y20IA==", | ||||||
|  |       "requires": { | ||||||
|  |         "uuid-random": "^1.3.2" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "iconv-lite": { |     "iconv-lite": { | ||||||
|       "version": "0.6.3", |       "version": "0.6.3", | ||||||
|       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", |       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", | ||||||
| @@ -3650,6 +3854,16 @@ | |||||||
|       "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", |       "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", | ||||||
|       "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" |       "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" | ||||||
|     }, |     }, | ||||||
|  |     "rrule": { | ||||||
|  |       "version": "2.7.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/rrule/-/rrule-2.7.2.tgz", | ||||||
|  |       "integrity": "sha512-NkBsEEB6FIZOZ3T8frvEBOB243dm46SPufpDckY/Ap/YH24V1zLeMmDY8OA10lk452NdrF621+ynDThE7FQU2A==", | ||||||
|  |       "optional": true, | ||||||
|  |       "peer": true, | ||||||
|  |       "requires": { | ||||||
|  |         "tslib": "^2.4.0" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "rw": { |     "rw": { | ||||||
|       "version": "1.3.3", |       "version": "1.3.3", | ||||||
|       "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", |       "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", | ||||||
| @@ -3835,6 +4049,13 @@ | |||||||
|         "punycode": "^2.1.1" |         "punycode": "^2.1.1" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "tslib": { | ||||||
|  |       "version": "2.5.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz", | ||||||
|  |       "integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==", | ||||||
|  |       "optional": true, | ||||||
|  |       "peer": true | ||||||
|  |     }, | ||||||
|     "type-check": { |     "type-check": { | ||||||
|       "version": "0.3.2", |       "version": "0.3.2", | ||||||
|       "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", |       "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", | ||||||
| @@ -3868,6 +4089,11 @@ | |||||||
|         "requires-port": "^1.0.0" |         "requires-port": "^1.0.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "uuid-random": { | ||||||
|  |       "version": "1.3.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/uuid-random/-/uuid-random-1.3.2.tgz", | ||||||
|  |       "integrity": "sha512-UOzej0Le/UgkbWEO8flm+0y+G+ljUon1QWTEZOq1rnMAsxo2+SckbiZdKzAHHlVh6gJqI1TjC/xwgR50MuCrBQ==" | ||||||
|  |     }, | ||||||
|     "vega": { |     "vega": { | ||||||
|       "version": "5.25.0", |       "version": "5.25.0", | ||||||
|       "resolved": "https://registry.npmjs.org/vega/-/vega-5.25.0.tgz", |       "resolved": "https://registry.npmjs.org/vega/-/vega-5.25.0.tgz", | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ | |||||||
|   }, |   }, | ||||||
|   "main": "./build/index.js", |   "main": "./build/index.js", | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|  |     "@types/ical": "^0.8.0", | ||||||
|     "@types/jsdom": "^20.0.1", |     "@types/jsdom": "^20.0.1", | ||||||
|     "@types/luxon": "^3.3.0", |     "@types/luxon": "^3.3.0", | ||||||
|     "@types/mustache": "^4.2.2", |     "@types/mustache": "^4.2.2", | ||||||
| @@ -25,6 +26,8 @@ | |||||||
|     "@doc-utils/jsonschema2markdown": "^0.1.1", |     "@doc-utils/jsonschema2markdown": "^0.1.1", | ||||||
|     "@doc-utils/markdown2html": "^0.2.1", |     "@doc-utils/markdown2html": "^0.2.1", | ||||||
|     "glob": "^10.2.3", |     "glob": "^10.2.3", | ||||||
|  |     "ical": "^0.8.0", | ||||||
|  |     "ical-generator": "^4.1.0", | ||||||
|     "luxon": "^3.3.0", |     "luxon": "^3.3.0", | ||||||
|     "mustache": "^4.2.0", |     "mustache": "^4.2.0", | ||||||
|     "xmlbuilder2": "^3.1.1", |     "xmlbuilder2": "^3.1.1", | ||||||
|   | |||||||
| @@ -1,12 +1,15 @@ | |||||||
|  |  | ||||||
| import { dirname, join as path_join } from 'path'; | import { dirname, join as path_join } from 'path'; | ||||||
| import { mkdirp, write_text } from '../fs'; |  | ||||||
| import { icons } from '../icons'; | import { icons } from '../icons'; | ||||||
| import { BuildState } from './state'; | import { BuildState } from './state'; | ||||||
| import { load_partials, FrontMatter, Context, load_layout, render_template } from '../template'; | import { mkdirp, write_text } from '../fs'; | ||||||
| import { render_theme_css_properties } from '../themes'; |  | ||||||
| import { render_markdown_to_html, render_markdown_to_html_inline_sync } from '@doc-utils/markdown2html'; |  | ||||||
| import { CalendarConfig, RSSConfig } from '../conf'; | import { CalendarConfig, RSSConfig } from '../conf'; | ||||||
|  | import { render_theme_css_properties } from '../themes'; | ||||||
|  | import { load_partials, FrontMatter, Context, load_layout, render_template } from '../template'; | ||||||
|  | import { render_markdown_to_html, render_markdown_to_html_inline_sync } from '@doc-utils/markdown2html'; | ||||||
|  | import { RSSEntry } from './rss'; | ||||||
|  | import { DateTime } from 'luxon'; | ||||||
|  | import { EventEntry } from './icalendar'; | ||||||
|  |  | ||||||
| export interface OutFileURL { | export interface OutFileURL { | ||||||
| 	base_url: string; | 	base_url: string; | ||||||
| @@ -84,8 +87,12 @@ export function mustache_context(state: BuildState, page_url: string, frontmatte | |||||||
| 		page: frontmatter, | 		page: frontmatter, | ||||||
| 		base_url: state.conf.base_url, | 		base_url: state.conf.base_url, | ||||||
| 		page_url: page_url, | 		page_url: page_url, | ||||||
|  | 		site_title: state.conf.title, | ||||||
|  | 		author: get_author(state, frontmatter), | ||||||
| 		build_time: state.build_time, | 		build_time: state.build_time, | ||||||
| 		icons: icons, | 		icons: icons, | ||||||
|  | 		rss_feeds: state.conf.rss || [ ], | ||||||
|  | 		calendars: state.conf.calendars || [ ], | ||||||
| 		themes: Object.values(state.themes), | 		themes: Object.values(state.themes), | ||||||
| 		theme_groups: structuredClone(state.theme_groups), | 		theme_groups: structuredClone(state.theme_groups), | ||||||
| 		markdown: { | 		markdown: { | ||||||
| @@ -102,7 +109,7 @@ export function mustache_context(state: BuildState, page_url: string, frontmatte | |||||||
| 	}; | 	}; | ||||||
| } | } | ||||||
|  |  | ||||||
| export async function render_page(state: BuildState, out_file: string, out_url: OutFileURL, text: string, render_as_markdown: boolean, frontmatter?: any) { | export async function render_page(state: BuildState, in_file: string, out_file: string, out_url: OutFileURL, text: string, render_as_markdown: boolean, frontmatter?: any) { | ||||||
| 	if (render_as_markdown) { | 	if (render_as_markdown) { | ||||||
| 		const opts = Object.assign({ }, state.conf.markdown, { | 		const opts = Object.assign({ }, state.conf.markdown, { | ||||||
| 			base_url: out_url.abs_url | 			base_url: out_url.abs_url | ||||||
| @@ -127,69 +134,107 @@ export async function render_page(state: BuildState, out_file: string, out_url: | |||||||
| 	const rendered = render_template(text, context, layout, structuredClone(state.partials), tags); | 	const rendered = render_template(text, context, layout, structuredClone(state.partials), tags); | ||||||
| 	await write_text(out_file, rendered); | 	await write_text(out_file, rendered); | ||||||
|  |  | ||||||
| 	handle_page_side_effects(state, out_file, out_url, frontmatter); | 	handle_page_side_effects(state, in_file, out_file, out_url, text, frontmatter); | ||||||
| } | } | ||||||
|  |  | ||||||
| function handle_page_side_effects(state: BuildState, out_file: string, out_url: OutFileURL, frontmatter?: any) { | function handle_page_side_effects(state: BuildState, in_file: string, out_file: string, out_url: OutFileURL, text: string, frontmatter?: any) { | ||||||
| 	// Only actual HTML webpages are registered with things like the RSS feed; This is | 	// Only actual HTML webpages are registered with things like the RSS feed; This is | ||||||
| 	// to prevent things like CSS files showing up, which may be templated (and therefore | 	// to prevent things like CSS files showing up, which may be templated (and therefore | ||||||
| 	// pass through this function), but are not really "pages" | 	// pass through this function), but are not really "pages" | ||||||
| 	if (frontmatter && typeof frontmatter === 'object' && out_file.endsWith('.html')) { | 	if (frontmatter && typeof frontmatter === 'object' && out_file.endsWith('.html')) { | ||||||
| 		if (state.conf.rss) { | 		if (state.conf.rss) { | ||||||
| 			if (Array.isArray(state.conf.rss)) { | 			for (const [index, rss_conf] of Object.entries(state.conf.rss)) { | ||||||
| 				for (const rss_conf of state.conf.rss) { | 				if (in_file.startsWith(rss_conf.in_dir + '/')) { | ||||||
| 					handle_rss(state, rss_conf, out_url, frontmatter); | 					const entries = (state.rss[index] = state.rss[index] || [ ]); | ||||||
| 				} | 					handle_rss(state, rss_conf, entries, in_file, out_url, text, frontmatter); | ||||||
| 			} | 				} | ||||||
|  |  | ||||||
| 			else { |  | ||||||
| 				handle_rss(state, { }, out_url, frontmatter); |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		if (state.conf.sitemap) { | ||||||
| 			handle_sitemap(state, out_url, frontmatter); | 			handle_sitemap(state, out_url, frontmatter); | ||||||
| 		handle_event(state, out_url, frontmatter); | 		} | ||||||
|  |  | ||||||
|  | 		if (frontmatter?.event) { | ||||||
|  | 			if (state.conf.events) { | ||||||
|  | 				handle_event(state, in_file, out_url, frontmatter); | ||||||
|  | 			} | ||||||
| 	 | 	 | ||||||
| 			if (state.conf.calendars) { | 			if (state.conf.calendars) { | ||||||
| 			for (const cal_conf of state.conf.calendars) { | 				for (const [index, cal_conf] of Object.entries(state.conf.calendars)) { | ||||||
| 				handle_calendar(state, cal_conf, out_url, frontmatter); | 					if (in_file.startsWith(cal_conf.in_dir + '/')) { | ||||||
|  | 						const entries = (state.rss[index] = state.rss[index] || [ ]); | ||||||
|  | 						handle_calendar(state, cal_conf, entries, in_file, out_url, frontmatter); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| function handle_rss(state: BuildState, rss_conf: RSSConfig, out_url: OutFileURL, frontmatter: any) { | function handle_rss(state: BuildState, rss_conf: RSSConfig, entries: RSSEntry[], in_file: string, out_url: OutFileURL, text: string, frontmatter: FrontMatter) { | ||||||
| 	// const field = rss_conf | 	const author_or_authors = get_author(state, frontmatter); | ||||||
| 	// const rss_frontmatter = state. | 	const author = Array.isArray(author_or_authors) ? author_or_authors[0] : author_or_authors; | ||||||
| 	// //  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function handle_sitemap(state: BuildState, out_url: OutFileURL, frontmatter: any) { | 	entries.push({ | ||||||
| 	if (! state.conf.sitemap) { |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	const sitemap_frontmatter = state.conf.sitemap.front_matter_field |  | ||||||
| 		? frontmatter?.[state.conf.sitemap.front_matter_field] || { } |  | ||||||
| 		: frontmatter?.sitemap || { }; |  | ||||||
|  |  | ||||||
| 	state.sitemap.push({ |  | ||||||
| 		url: out_url.abs_url, | 		url: out_url.abs_url, | ||||||
| 		lastmod: state.build_time.iso, | 		in_file: in_file, | ||||||
| 		change_freq: sitemap_frontmatter?.change_freq, | 		html_content: text, | ||||||
| 		priority: sitemap_frontmatter?.priority, | 		title: frontmatter?.title, | ||||||
|  | 		description: frontmatter?.description, | ||||||
|  | 		author_name: author?.name, | ||||||
|  | 		tags: frontmatter?.tags, | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
|  |  | ||||||
| function handle_event(state: BuildState, out_url: OutFileURL, frontmatter: any) { | function handle_sitemap(state: BuildState, out_url: OutFileURL, frontmatter: FrontMatter) { | ||||||
| 	//  | 	state.sitemap.push({ | ||||||
|  | 		url: out_url.abs_url, | ||||||
|  | 		lastmod: state.build_time.iso, | ||||||
|  | 		change_freq: frontmatter?.sitemap?.change_freq, | ||||||
|  | 		priority: frontmatter?.sitemap?.priority, | ||||||
|  | 	}); | ||||||
| } | } | ||||||
|  |  | ||||||
| function handle_calendar(state: BuildState, cal_conf: CalendarConfig, out_url: OutFileURL, frontmatter: any) { | function handle_event(state: BuildState, in_file: string, out_url: OutFileURL, frontmatter: FrontMatter) { | ||||||
| 	//  | 	if (! state.conf.events) { | ||||||
|  | 		return; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| export function file_hash_matches(state: BuildState, in_file: string, new_hash: string) { | 	const author_or_authors = get_author(state, frontmatter); | ||||||
|  | 	const author = Array.isArray(author_or_authors) ? author_or_authors[0] : author_or_authors; | ||||||
|  |  | ||||||
|  | 	state.events.push({ | ||||||
|  | 		url: out_url.abs_url, | ||||||
|  | 		in_file: in_file, | ||||||
|  | 		title: frontmatter.title, | ||||||
|  | 		description: frontmatter.description, | ||||||
|  | 		author_name: author?.name, | ||||||
|  | 		author_email: author?.email, | ||||||
|  | 		start_time: frontmatter.event?.start_time, | ||||||
|  | 		end_time: frontmatter.event?.end_time, | ||||||
|  | 		time_zone: frontmatter.event?.time_zone, | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function handle_calendar(state: BuildState, cal_conf: CalendarConfig, entries: EventEntry[], in_file: string, out_url: OutFileURL, frontmatter: FrontMatter) { | ||||||
|  | 	const author_or_authors = get_author(state, frontmatter); | ||||||
|  | 	const author = Array.isArray(author_or_authors) ? author_or_authors[0] : author_or_authors; | ||||||
|  |  | ||||||
|  | 	entries.push({ | ||||||
|  | 		url: out_url.abs_url, | ||||||
|  | 		in_file: in_file, | ||||||
|  | 		title: frontmatter.title, | ||||||
|  | 		description: frontmatter.description, | ||||||
|  | 		author_name: author?.name, | ||||||
|  | 		author_email: author?.email, | ||||||
|  | 		start_time: frontmatter.event?.start_time, | ||||||
|  | 		end_time: frontmatter.event?.end_time, | ||||||
|  | 		time_zone: frontmatter.event?.time_zone, | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function config_hash_matches(state: BuildState) { | ||||||
| 	const { old_metadata, new_metadata } = state; | 	const { old_metadata, new_metadata } = state; | ||||||
|  |  | ||||||
| 	if (! old_metadata.last_build?.config_hash) { | 	if (! old_metadata.last_build?.config_hash) { | ||||||
| @@ -200,6 +245,10 @@ export function file_hash_matches(state: BuildState, in_file: string, new_hash: | |||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function file_hash_matches(state: BuildState, in_file: string, new_hash: string) { | ||||||
| 	in_file = in_file.slice(state.conf.input.root.length); | 	in_file = in_file.slice(state.conf.input.root.length); | ||||||
| 	const old_hash = state.old_metadata.files[in_file]?.last_build_hash; | 	const old_hash = state.old_metadata.files[in_file]?.last_build_hash; | ||||||
| 	 | 	 | ||||||
| @@ -217,7 +266,12 @@ export function file_hash_matches(state: BuildState, in_file: string, new_hash: | |||||||
| export function skip_file(state: BuildState, in_file: string, out_file: string, out_url: OutFileURL, frontmatter?: any) { | export function skip_file(state: BuildState, in_file: string, out_file: string, out_url: OutFileURL, frontmatter?: any) { | ||||||
| 	in_file = in_file.slice(state.conf.input.root.length); | 	in_file = in_file.slice(state.conf.input.root.length); | ||||||
| 	state.new_metadata.files[in_file] = structuredClone(state.old_metadata?.files?.[in_file]); | 	state.new_metadata.files[in_file] = structuredClone(state.old_metadata?.files?.[in_file]); | ||||||
| 	handle_page_side_effects(state, out_file, out_url, frontmatter); | 	handle_page_side_effects(state, in_file, out_file, out_url, frontmatter); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function copy_metadata(state: BuildState, in_file: string) { | ||||||
|  | 	in_file = in_file.slice(state.conf.input.root.length); | ||||||
|  | 	state.new_metadata.files[in_file] = structuredClone(state.old_metadata?.files?.[in_file]); | ||||||
| } | } | ||||||
|  |  | ||||||
| export function update_metadata(state: BuildState, in_file: string, hash: string) { | export function update_metadata(state: BuildState, in_file: string, hash: string) { | ||||||
| @@ -228,3 +282,23 @@ export function update_metadata(state: BuildState, in_file: string, hash: string | |||||||
| 		last_updated_time: state.build_time.iso, | 		last_updated_time: state.build_time.iso, | ||||||
| 	}; | 	}; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export function get_author(state: BuildState, frontmatter?: FrontMatter) { | ||||||
|  | 	if (! frontmatter?.author) { | ||||||
|  | 		return null; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (Array.isArray(frontmatter.author)) { | ||||||
|  | 		const list = frontmatter.author | ||||||
|  | 			.map((author) => state.conf.authors?.[author]) | ||||||
|  | 			.filter((author) => author); | ||||||
|  |  | ||||||
|  | 		if (! list.length) { | ||||||
|  | 			return null; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return list; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return state.conf.authors?.[frontmatter.author] | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										131
									
								
								src/build-files/icalendar.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/build-files/icalendar.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,131 @@ | |||||||
|  |  | ||||||
|  | import { write_text } from '../fs'; | ||||||
|  | import { BuildState } from './state'; | ||||||
|  | import { map_input_file_to_output_file } from './helpers'; | ||||||
|  | import { parseICS, CalendarComponent } from 'ical'; | ||||||
|  | import create_calendar, { ICalEventData, ICalCalendarData } from 'ical-generator'; | ||||||
|  |  | ||||||
|  | export type { ICalEventData, ICalCalendarData, ICalAttendeeData, ICalAttendeeStatus } from 'ical-generator'; | ||||||
|  |  | ||||||
|  | export interface EventEntry { | ||||||
|  | 	url: string; | ||||||
|  | 	in_file: string; | ||||||
|  | 	title?: string; | ||||||
|  | 	description?: string; | ||||||
|  | 	author_name?: string; | ||||||
|  | 	author_email?: string; | ||||||
|  | 	start_time?: string; | ||||||
|  | 	end_time?: string; | ||||||
|  | 	time_zone?: `${string}/${string}`; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export async function write_events_and_calendars_if_needed(state: BuildState) { | ||||||
|  | 	if (state.conf.events) { | ||||||
|  | 		for (const entry of state.events) { | ||||||
|  | 			const event = icalendar_event(state, entry); | ||||||
|  | 			const cal_data: ICalCalendarData = { | ||||||
|  | 				prodId: { | ||||||
|  | 					company: 'jbrumond.me', | ||||||
|  | 					product: 'docs2website', | ||||||
|  | 					language: 'EN', | ||||||
|  | 				}, | ||||||
|  | 				// ... | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			const calendar = create_icalendar(cal_data, event); | ||||||
|  | 			const out_file = await map_input_file_to_output_file(state, entry.in_file, [ '.html', '.md', '.markdown' ], '.isc'); | ||||||
|  | 			await write_text(out_file, calendar); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (state.conf.calendars) { | ||||||
|  | 		for (let index = 0; index < state.conf.calendars.length; index++) { | ||||||
|  | 			const cal_conf = state.conf.calendars[index]; | ||||||
|  | 			const cal_entries = state.calendars[index]; | ||||||
|  |  | ||||||
|  | 			for (const entry of cal_entries) { | ||||||
|  | 				const event = icalendar_event(state, entry); | ||||||
|  | 				const cal_data: ICalCalendarData = { | ||||||
|  | 					name: cal_conf.title, | ||||||
|  | 					prodId: { | ||||||
|  | 						company: 'jbrumond.me', | ||||||
|  | 						product: 'docs2website', | ||||||
|  | 						language: 'EN', | ||||||
|  | 					}, | ||||||
|  | 					// ... | ||||||
|  | 				}; | ||||||
|  |  | ||||||
|  | 				const calendar = create_icalendar(cal_data, event); | ||||||
|  | 				await write_text(cal_conf.out_file, calendar); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function parse_icalendar(contents: string) { | ||||||
|  | 	const parsed = parseICS(contents); | ||||||
|  | 	const calendar: CalendarComponent[] = [ ]; | ||||||
|  |  | ||||||
|  | 	for (const data of Object.values(parsed)) { | ||||||
|  | 		calendar.push(data); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return calendar; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function create_icalendar(cal: ICalCalendarData, events: ICalEventData | ICalEventData[]) { | ||||||
|  | 	const calendar = create_calendar(cal); | ||||||
|  |  | ||||||
|  | 	if (Array.isArray(events)) { | ||||||
|  | 		for (const event of events) { | ||||||
|  | 			calendar.createEvent(event); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	else { | ||||||
|  | 		calendar.createEvent(events); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return calendar.toString(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function icalendar_event(state: BuildState, entry: EventEntry) : ICalEventData { | ||||||
|  | 	const in_file = entry.in_file.slice(state.conf.input.root.length); | ||||||
|  | 	const metadata = state.new_metadata.files[in_file]; | ||||||
|  |  | ||||||
|  | 	return { | ||||||
|  | 		id: entry.url, | ||||||
|  | 		summary: entry.title, | ||||||
|  | 		description: entry.description || void 0, | ||||||
|  | 		start: entry.start_time, | ||||||
|  | 		end: entry.end_time, | ||||||
|  | 		url: entry.url, | ||||||
|  | 		timezone: entry.time_zone, | ||||||
|  | 		created: metadata.first_seen_time, | ||||||
|  | 		lastModified: metadata.last_updated_time, | ||||||
|  | 		organizer: { | ||||||
|  | 			name: entry.author_name || 'Unknown', | ||||||
|  | 			email: entry.author_email, | ||||||
|  | 		}, | ||||||
|  | 		// attendees: post.mentions.flatMap((mention) : ICalAttendeeData | ICalAttendeeData[] => { | ||||||
|  | 		// 	if (mention.is_rsvp && mention.is_reply_to_this) { | ||||||
|  | 		// 		const ext = mention.external as ExternalEntry; | ||||||
|  | 		// 		const status = ext.rsvp_type === 'yes' | ||||||
|  | 		// 			? 'ACCEPTED' as const | ||||||
|  | 		// 			: ext.rsvp_type === 'no' | ||||||
|  | 		// 				? 'DECLINED' as const | ||||||
|  | 		// 				: ext.rsvp_type === 'maybe' | ||||||
|  | 		// 					? 'TENTATIVE' as const | ||||||
|  | 		// 					: 'NEEDS-ACTION' as const; | ||||||
|  |  | ||||||
|  | 		// 		return { | ||||||
|  | 		// 			name: mention.author_name, | ||||||
|  | 		// 			rsvp: ext.rsvp_type === 'yes', | ||||||
|  | 		// 			status: status as ICalAttendeeStatus | ||||||
|  | 		// 		}; | ||||||
|  | 		// 	} | ||||||
|  |  | ||||||
|  | 		// 	return [ ]; | ||||||
|  | 		// }), | ||||||
|  | 	}; | ||||||
|  | } | ||||||
| @@ -14,6 +14,8 @@ import { render_text_file_templates } from './mustache'; | |||||||
| import { render_markdown_files } from './markdown'; | import { render_markdown_files } from './markdown'; | ||||||
| import { render_json_schema_files } from './jsonschema'; | import { render_json_schema_files } from './jsonschema'; | ||||||
| import { write_sitemap_if_needed } from './sitemap'; | import { write_sitemap_if_needed } from './sitemap'; | ||||||
|  | import { write_rss_if_needed } from './rss'; | ||||||
|  | import { write_events_and_calendars_if_needed } from './icalendar'; | ||||||
|  |  | ||||||
| export { BuildState, ThemeGroups } from './state'; | export { BuildState, ThemeGroups } from './state'; | ||||||
|  |  | ||||||
| @@ -55,7 +57,10 @@ export async function build_docs_project(conf: Config) { | |||||||
| 		}, | 		}, | ||||||
| 		extras: await load_extras(), | 		extras: await load_extras(), | ||||||
| 		made_directories: new Set<string>(), | 		made_directories: new Set<string>(), | ||||||
|  | 		rss: [ ], | ||||||
| 		sitemap: [ ], | 		sitemap: [ ], | ||||||
|  | 		events: [ ], | ||||||
|  | 		calendars: [ ], | ||||||
| 		build_time: { | 		build_time: { | ||||||
| 			iso: now.toISO(), | 			iso: now.toISO(), | ||||||
| 			rfc2822: now.toRFC2822(), | 			rfc2822: now.toRFC2822(), | ||||||
| @@ -87,8 +92,8 @@ export async function build_docs_project(conf: Config) { | |||||||
| 	// todo: other file types... | 	// todo: other file types... | ||||||
|  |  | ||||||
| 	await write_sitemap_if_needed(state); | 	await write_sitemap_if_needed(state); | ||||||
| 	// todo: rss | 	await write_rss_if_needed(state); | ||||||
| 	// todo: events | 	await write_events_and_calendars_if_needed(state); | ||||||
|  |  | ||||||
| 	// Write the updated metadata file | 	// Write the updated metadata file | ||||||
| 	await write_json(conf.metadata, state.new_metadata, true); | 	await write_json(conf.metadata, state.new_metadata, true); | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ import { BuildState } from './state'; | |||||||
| import { read_json, write_text, read_yaml } from '../fs'; | import { read_json, write_text, read_yaml } from '../fs'; | ||||||
| import { build_markdown_from_json_schema } from '@doc-utils/jsonschema2markdown'; | import { build_markdown_from_json_schema } from '@doc-utils/jsonschema2markdown'; | ||||||
| import { stringify as to_yaml } from 'yaml'; | import { stringify as to_yaml } from 'yaml'; | ||||||
| import { build_partials, file_hash_matches, map_input_file_to_output_file, map_output_file_to_url, render_page, skip_file, update_metadata } from './helpers'; | import { build_partials, copy_metadata, file_hash_matches, map_input_file_to_output_file, map_output_file_to_url, render_page, skip_file, update_metadata } from './helpers'; | ||||||
|  |  | ||||||
| export async function render_json_schema_files(state: BuildState) { | export async function render_json_schema_files(state: BuildState) { | ||||||
| 	const promises: Promise<any>[] = [ ]; | 	const promises: Promise<any>[] = [ ]; | ||||||
| @@ -113,10 +113,6 @@ export async function render_json_schema(state: BuildState, schema: unknown, in_ | |||||||
|  |  | ||||||
| 	const out_url = map_output_file_to_url(state, out_file); | 	const out_url = map_output_file_to_url(state, out_file); | ||||||
|  |  | ||||||
| 	if (file_hash_matches(state, in_file, hash)) { |  | ||||||
| 		return skip_file(state, in_file, out_file, out_url, frontmatter); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	const promises: Promise<any>[] = [ ]; | 	const promises: Promise<any>[] = [ ]; | ||||||
| 	const markdown = build_markdown_from_json_schema(schema); | 	const markdown = build_markdown_from_json_schema(schema); | ||||||
| 	 | 	 | ||||||
| @@ -128,9 +124,14 @@ export async function render_json_schema(state: BuildState, schema: unknown, in_ | |||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	promises.push( | 	promises.push( | ||||||
| 		render_page(state, out_file, out_url, markdown, true, frontmatter) | 		render_page(state, in_file, out_file, out_url, markdown, true, frontmatter) | ||||||
| 	); | 	); | ||||||
| 	 | 	 | ||||||
| 	await Promise.all(promises); | 	await Promise.all(promises); | ||||||
|  |  | ||||||
|  | 	if (file_hash_matches(state, in_file, hash)) { | ||||||
|  | 		return copy_metadata(state, in_file); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	update_metadata(state, in_file, hash); | 	update_metadata(state, in_file, hash); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
| import { glob } from 'glob'; | import { glob } from 'glob'; | ||||||
| import { read_text } from '../fs'; | import { read_text } from '../fs'; | ||||||
| import { BuildState } from './state'; | import { BuildState } from './state'; | ||||||
| import { build_partials, file_hash_matches, map_input_file_to_output_file, map_output_file_to_url, render_page, skip_file, update_metadata } from './helpers'; | import { build_partials, copy_metadata, file_hash_matches, map_input_file_to_output_file, map_output_file_to_url, render_page, skip_file, update_metadata } from './helpers'; | ||||||
|  |  | ||||||
| export async function render_markdown_files(state: BuildState) { | export async function render_markdown_files(state: BuildState) { | ||||||
| 	const promises: Promise<any>[] = [ ]; | 	const promises: Promise<any>[] = [ ]; | ||||||
| @@ -39,10 +39,11 @@ export async function render_markdown_file(state: BuildState, in_file: string) { | |||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	await render_page(state, in_file, out_file, out_url, text, true, frontmatter); | ||||||
|  |  | ||||||
| 	if (file_hash_matches(state, in_file, hash)) { | 	if (file_hash_matches(state, in_file, hash)) { | ||||||
| 		return skip_file(state, in_file, out_file, out_url, frontmatter); | 		return copy_metadata(state, in_file); | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	await render_page(state, out_file, out_url, text, true, frontmatter); |  | ||||||
| 	update_metadata(state, in_file, hash); | 	update_metadata(state, in_file, hash); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
| import { glob } from 'glob'; | import { glob } from 'glob'; | ||||||
| import { read_text } from '../fs'; | import { read_text } from '../fs'; | ||||||
| import { BuildState } from './state'; | import { BuildState } from './state'; | ||||||
| import { build_partials, file_hash_matches, map_input_file_to_output_file, map_output_file_to_url, render_page, skip_file, update_metadata } from './helpers'; | import { build_partials, copy_metadata, file_hash_matches, map_input_file_to_output_file, map_output_file_to_url, render_page, skip_file, update_metadata } from './helpers'; | ||||||
|  |  | ||||||
| export async function render_text_file_templates(state: BuildState) { | export async function render_text_file_templates(state: BuildState) { | ||||||
| 	const promises: Promise<any>[] = [ ]; | 	const promises: Promise<any>[] = [ ]; | ||||||
| @@ -39,10 +39,11 @@ export async function render_text_file_template(state: BuildState, in_file: stri | |||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	await render_page(state, in_file, out_file, out_url, text, false, frontmatter); | ||||||
|  |  | ||||||
| 	if (file_hash_matches(state, in_file, hash)) { | 	if (file_hash_matches(state, in_file, hash)) { | ||||||
| 		return skip_file(state, in_file, out_file, out_url, frontmatter); | 		return copy_metadata(state, in_file); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	await render_page(state, out_file, out_url, text, false, frontmatter); |  | ||||||
| 	update_metadata(state, in_file, hash); | 	update_metadata(state, in_file, hash); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										131
									
								
								src/build-files/rss.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/build-files/rss.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,131 @@ | |||||||
|  |  | ||||||
|  | import type { XMLBuilder } from 'xmlbuilder2/lib/interfaces'; | ||||||
|  | import { create as create_xml } from 'xmlbuilder2'; | ||||||
|  | import { BuildState } from './state'; | ||||||
|  | import { write_text } from '../fs'; | ||||||
|  | import { app_version } from '../conf'; | ||||||
|  | import { map_output_file_to_url } from './helpers'; | ||||||
|  | import { DateTime } from 'luxon'; | ||||||
|  |  | ||||||
|  | export interface RSSEntry { | ||||||
|  | 	url: string; | ||||||
|  | 	in_file: string; | ||||||
|  | 	title?: string; | ||||||
|  | 	description?: string; | ||||||
|  | 	author_name?: string; | ||||||
|  | 	html_content?: string; | ||||||
|  | 	tags?: string[]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export async function write_rss_if_needed(state: BuildState) { | ||||||
|  | 	if (! state.conf.rss) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for (let index = 0; index < state.conf.rss.length; index++) { | ||||||
|  | 		const rss_conf = state.conf.rss[index]; | ||||||
|  | 		const doc = create_xml({ version: '1.0', encoding: 'UTF-8' }); | ||||||
|  | 		const { abs_url: self_url } = map_output_file_to_url(state, rss_conf.out_file); | ||||||
|  | 	 | ||||||
|  | 		if (rss_conf.xsl) { | ||||||
|  | 			for (const url of rss_conf.xsl) { | ||||||
|  | 				doc.ins('xml-stylesheet', `href="${url}" type="text/xsl"`); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		const rss = doc.ele('rss', { | ||||||
|  | 			version: '2.0', | ||||||
|  | 			'xmlns:dc': 'http://purl.org/dc/elements/1.1/', | ||||||
|  | 			'xmlns:content': 'http://purl.org/rss/1.0/modules/content/', | ||||||
|  | 			'xmlns:atom': 'http://www.w3.org/2005/Atom', | ||||||
|  | 			// 'xmlns:docs2website': 'urn:uuid:7fc4e5d4-f68e-11ed-b0d0-00155ddef564', | ||||||
|  | 		}); | ||||||
|  |  | ||||||
|  | 		const channel = rss.ele('channel'); | ||||||
|  | 		channel.ele('generator').txt(`docs2website ${app_version}`); | ||||||
|  |  | ||||||
|  | 		if (rss_conf.title) { | ||||||
|  | 			channel.ele('title').txt(rss_conf.title); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (rss_conf.link) { | ||||||
|  | 			channel.ele('link').txt(rss_conf.link); | ||||||
|  | 		} | ||||||
|  | 	 | ||||||
|  | 		link(channel, 'application/rss+xml', 'self', self_url); | ||||||
|  |  | ||||||
|  | 		if (rss_conf.alternates?.length) { | ||||||
|  | 			for (const alt of rss_conf.alternates) { | ||||||
|  | 				link(channel, alt.type, 'alternate', alt.href); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	 | ||||||
|  | 		if (rss_conf.description) { | ||||||
|  | 			channel.ele('description').txt(rss_conf.description); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if (rss_conf.language) { | ||||||
|  | 			channel.ele('language').txt(rss_conf.language); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if (rss_conf.copyright) { | ||||||
|  | 			channel.ele('copyright').txt(rss_conf.copyright); | ||||||
|  | 		} | ||||||
|  | 	 | ||||||
|  | 		channel.ele('lastBuildDate').txt(state.build_time.rfc2822); | ||||||
|  |  | ||||||
|  | 		// channel.ele('rating').txt('');  // see: https://www.w3.org/PICS/ | ||||||
|  | 		// channel.ele('pubDate').txt(publish_date.toUTCString()); | ||||||
|  | 		// channel.ele('categories').txt(''); | ||||||
|  | 		// channel.ele('docs').txt(''); | ||||||
|  | 		// channel.ele('managingEditor').txt('james@jbrumond.me'); | ||||||
|  | 		// channel.ele('webMaster').txt('james@jbrumond.me'); | ||||||
|  | 		// channel.ele('ttl').txt(''); | ||||||
|  | 		// channel.ele('image').txt(''); | ||||||
|  | 		// channel.ele('skipHours').txt(''); | ||||||
|  | 		// channel.ele('skipDays').txt(''); | ||||||
|  | 	 | ||||||
|  | 		for (const entry of state.rss[index] || [ ]) { | ||||||
|  | 			const item = channel.ele('item'); | ||||||
|  | 			const in_file = entry.in_file.slice(state.conf.input.root.length); | ||||||
|  | 			const metadata = state.new_metadata.files[in_file]; | ||||||
|  | 	 | ||||||
|  | 			item.ele('link').txt(entry.url); | ||||||
|  |  | ||||||
|  | 			if (entry.title) { | ||||||
|  | 				item.ele('title').txt(entry.title); | ||||||
|  | 			} | ||||||
|  | 	 | ||||||
|  | 			if (entry.description) { | ||||||
|  | 				item.ele('description').txt(entry.description); | ||||||
|  | 			} | ||||||
|  | 			 | ||||||
|  | 			if (entry.author_name) { | ||||||
|  | 				item.ele('dc:creator').ele({ $: entry.author_name }); | ||||||
|  | 			} | ||||||
|  | 	 | ||||||
|  | 			if (entry.tags) { | ||||||
|  | 				entry.tags.forEach((tag) => item.ele('category').txt(tag)); | ||||||
|  | 			} | ||||||
|  | 	 | ||||||
|  | 			item.ele('guid').txt(entry.url); | ||||||
|  | 			item.ele('pubDate').txt(DateTime.fromISO(metadata.first_seen_time).toRFC2822()); | ||||||
|  |  | ||||||
|  | 			if (entry.html_content) { | ||||||
|  | 				item.ele('content:encoded').ele({ $: entry.html_content }); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	 | ||||||
|  | 		const xml = doc.toString({ | ||||||
|  | 			indent: '  ', | ||||||
|  | 			prettyPrint: true, | ||||||
|  | 		}); | ||||||
|  | 	 | ||||||
|  | 		await write_text(rss_conf.out_file, xml); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 	 | ||||||
|  | function link(channel: XMLBuilder, type: string, rel: string, href: string) { | ||||||
|  | 	// channel.ele('link', { href, rel, type, }); | ||||||
|  | 	channel.ele('atom:link', { type, rel, href, }); | ||||||
|  | } | ||||||
| @@ -3,6 +3,8 @@ import type { Config } from '../conf'; | |||||||
| import type { Metadata } from '../metadata'; | import type { Metadata } from '../metadata'; | ||||||
| import type { ColorTheme } from '@doc-utils/color-themes'; | import type { ColorTheme } from '@doc-utils/color-themes'; | ||||||
| import type { SitemapEntry } from './sitemap'; | import type { SitemapEntry } from './sitemap'; | ||||||
|  | import { RSSEntry } from './rss'; | ||||||
|  | import { EventEntry } from './icalendar'; | ||||||
|  |  | ||||||
| export interface BuildState { | export interface BuildState { | ||||||
| 	conf: Config; | 	conf: Config; | ||||||
| @@ -16,12 +18,10 @@ export interface BuildState { | |||||||
| 	made_directories: Set<string>; | 	made_directories: Set<string>; | ||||||
| 	old_metadata: Metadata; | 	old_metadata: Metadata; | ||||||
| 	new_metadata: Metadata; | 	new_metadata: Metadata; | ||||||
| 	// rss: { | 	rss: RSSEntry[][]; | ||||||
| 	// 	url: string; |  | ||||||
| 	// 	last_updated: string; |  | ||||||
| 	// }[]; |  | ||||||
| 	sitemap: SitemapEntry[]; | 	sitemap: SitemapEntry[]; | ||||||
| 	// events: EventEntry[]; | 	events: EventEntry[]; | ||||||
|  | 	calendars: EventEntry[][]; | ||||||
| 	build_time: { | 	build_time: { | ||||||
| 		iso: string; | 		iso: string; | ||||||
| 		rfc2822: string; | 		rfc2822: string; | ||||||
|   | |||||||
							
								
								
									
										58
									
								
								src/conf.ts
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								src/conf.ts
									
									
									
									
									
								
							| @@ -4,31 +4,59 @@ import { parse as parse_yaml } from 'yaml'; | |||||||
| import { resolve as resolve_path, dirname } from 'path'; | import { resolve as resolve_path, dirname } from 'path'; | ||||||
| import { MarkdownOptions } from '@doc-utils/markdown2html'; | import { MarkdownOptions } from '@doc-utils/markdown2html'; | ||||||
|  |  | ||||||
|  | export const app_version = require('../package.json').version; | ||||||
|  |  | ||||||
| export async function read_config(file: string) { | export async function read_config(file: string) { | ||||||
| 	const path = resolve_path(process.cwd(), file); | 	const path = resolve_path(process.cwd(), file); | ||||||
| 	const yaml = await fs.readFile(path, 'utf8'); | 	const yaml = await fs.readFile(path, 'utf8'); | ||||||
| 	const config = parse_yaml(yaml); | 	const config = parse_yaml(yaml); | ||||||
|  | 	 | ||||||
| 	validate_config(config); | 	validate_config(config); | ||||||
|  |  | ||||||
|  | 	config.title = resolve_env_var_if_needed(config.title); | ||||||
|  | 	config.metadata = resolve_env_var_if_needed(config.metadata); | ||||||
|  | 	config.base_url = resolve_env_var_if_needed(config.base_url); | ||||||
|  | 	config.input.root = resolve_env_var_if_needed(config.input.root); | ||||||
|  | 	config.output.root = resolve_env_var_if_needed(config.output.root); | ||||||
|  |  | ||||||
| 	process_markdown_config(config); | 	process_markdown_config(config); | ||||||
| 	resolve_paths(path, config); | 	resolve_paths(path, config); | ||||||
|  | 	 | ||||||
| 	return config; | 	return config; | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface RSSConfig { | export interface RSSConfig { | ||||||
| 	dir_path?: string; | 	in_dir: string; | ||||||
| 	out_file?: string; | 	out_file: string; | ||||||
| 	front_matter_field?: string; | 	title?: string; | ||||||
|  | 	description?: string; | ||||||
|  | 	link?: string; | ||||||
|  | 	xsl?: string[]; | ||||||
|  | 	language?: string; | ||||||
|  | 	copyright?: string; | ||||||
|  | 	alternates?: { | ||||||
|  | 		type: string; | ||||||
|  | 		href: string; | ||||||
|  | 	}[]; | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface CalendarConfig { | export interface CalendarConfig { | ||||||
| 	dir_path?: string; | 	in_dir?: string; | ||||||
| 	out_file?: string; | 	out_file?: string; | ||||||
| 	front_matter_field?: string; | 	title?: string; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export interface AuthorConfig { | ||||||
|  | 	name?: string; | ||||||
|  | 	email?: string; | ||||||
|  | 	url?: string; | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface Config { | export interface Config { | ||||||
|  | 	title: string; | ||||||
| 	metadata: string; | 	metadata: string; | ||||||
| 	base_url: string; | 	base_url: string; | ||||||
|  | 	authors?: Record<string, AuthorConfig>; | ||||||
| 	input: { | 	input: { | ||||||
| 		root: string; | 		root: string; | ||||||
| 		raw?: string[]; | 		raw?: string[]; | ||||||
| @@ -55,11 +83,9 @@ export interface Config { | |||||||
| 	}; | 	}; | ||||||
| 	sitemap?: false | { | 	sitemap?: false | { | ||||||
| 		out_file?: string; | 		out_file?: string; | ||||||
| 		front_matter_field?: string; |  | ||||||
| 	}; | 	}; | ||||||
| 	rss?: false | RSSConfig[]; | 	rss?: false | RSSConfig[]; | ||||||
| 	events?: false | { | 	events?: false | { | ||||||
| 		front_matter_field?: string; |  | ||||||
| 	}; | 	}; | ||||||
| 	calendars?: false | CalendarConfig[]; | 	calendars?: false | CalendarConfig[]; | ||||||
| 	markdown?: Omit<MarkdownOptions, 'base_url' | 'inline' | 'extensions'>; | 	markdown?: Omit<MarkdownOptions, 'base_url' | 'inline' | 'extensions'>; | ||||||
| @@ -68,7 +94,7 @@ export interface Config { | |||||||
| 	}; | 	}; | ||||||
| 	openapi?: { | 	openapi?: { | ||||||
| 		// ; | 		// ; | ||||||
| 	} | 	}; | ||||||
| 	// ... | 	// ... | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -100,8 +126,8 @@ function resolve_paths(file_path: string, config: Config) { | |||||||
|  |  | ||||||
| 	if (Array.isArray(config.rss)) { | 	if (Array.isArray(config.rss)) { | ||||||
| 		for (const rss_conf of config.rss) { | 		for (const rss_conf of config.rss) { | ||||||
| 			if (rss_conf.dir_path) { | 			if (rss_conf.in_dir) { | ||||||
| 				rss_conf.dir_path = resolve_path(config.input.root, rss_conf.dir_path); | 				rss_conf.in_dir = resolve_path(config.input.root, rss_conf.in_dir); | ||||||
| 				rss_conf.out_file = resolve_path(config.output.root, rss_conf.out_file); | 				rss_conf.out_file = resolve_path(config.output.root, rss_conf.out_file); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @@ -113,8 +139,8 @@ function resolve_paths(file_path: string, config: Config) { | |||||||
|  |  | ||||||
| 	if (Array.isArray(config.calendars)) { | 	if (Array.isArray(config.calendars)) { | ||||||
| 		for (const cal_conf of config.calendars) { | 		for (const cal_conf of config.calendars) { | ||||||
| 			if (cal_conf.dir_path) { | 			if (cal_conf.in_dir) { | ||||||
| 				cal_conf.dir_path = resolve_path(config.input.root, cal_conf.dir_path); | 				cal_conf.in_dir = resolve_path(config.input.root, cal_conf.in_dir); | ||||||
| 				cal_conf.out_file = resolve_path(config.output.root, cal_conf.out_file); | 				cal_conf.out_file = resolve_path(config.output.root, cal_conf.out_file); | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @@ -136,3 +162,11 @@ function process_markdown_config(config: any) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function resolve_env_var_if_needed<T>(value: T | { from_env: string }) : T | string { | ||||||
|  | 	if (typeof value === 'object' && 'from_env' in value) { | ||||||
|  | 		return process.env[value.from_env]; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return value; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,16 +1,20 @@ | |||||||
|  |  | ||||||
| export interface Metadata { | export interface Metadata { | ||||||
| 	last_build: { | 	last_build: BuildMetadata; | ||||||
| 		time: string; |  | ||||||
| 		config_hash: string; |  | ||||||
| 	}; |  | ||||||
| 	files: { | 	files: { | ||||||
| 		[file_path: string]: { | 		[file_path: string]: FileMetadata; | ||||||
| 			first_seen_time: string; |  | ||||||
| 			last_build_hash: string; |  | ||||||
| 			last_updated_time: string; |  | ||||||
| 		}; |  | ||||||
| 	}; | 	}; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export interface BuildMetadata { | ||||||
|  | 	time: string; | ||||||
|  | 	config_hash: string; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export interface FileMetadata { | ||||||
|  | 	first_seen_time: string; | ||||||
|  | 	last_build_hash: string; | ||||||
|  | 	last_updated_time: string; | ||||||
|  | } | ||||||
|  |  | ||||||
| //  | //  | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
|  |  | ||||||
| import { Config } from './conf'; | import { AuthorConfig, CalendarConfig, Config, RSSConfig } from './conf'; | ||||||
| import { render as mustache_render } from 'mustache'; | import { render as mustache_render } from 'mustache'; | ||||||
| import { promises as fs } from 'fs'; | import { promises as fs } from 'fs'; | ||||||
| import { resolve as resolve_path } from 'path'; | import { resolve as resolve_path } from 'path'; | ||||||
| @@ -7,15 +7,20 @@ import { glob } from 'glob'; | |||||||
| import { load_from_dir } from './fs'; | import { load_from_dir } from './fs'; | ||||||
| import { ColorTheme } from '@doc-utils/color-themes'; | import { ColorTheme } from '@doc-utils/color-themes'; | ||||||
| import { ThemeGroups } from './build-files'; | import { ThemeGroups } from './build-files'; | ||||||
|  | import { ChangeFreq } from './build-files/sitemap'; | ||||||
|  |  | ||||||
| export interface Context { | export interface Context { | ||||||
| 	env?: Record<string, string>; | 	env?: Record<string, string>; | ||||||
| 	page?: FrontMatter; | 	page?: FrontMatter; | ||||||
| 	base_url: string; | 	base_url: string; | ||||||
| 	page_url: string; | 	page_url: string; | ||||||
|  | 	site_title: string; | ||||||
|  | 	author: AuthorConfig | AuthorConfig[]; | ||||||
| 	icons: Record<string, string>; | 	icons: Record<string, string>; | ||||||
| 	themes: ColorTheme[]; | 	themes: ColorTheme[]; | ||||||
| 	theme_groups: ThemeGroups; | 	theme_groups: ThemeGroups; | ||||||
|  | 	rss_feeds: RSSConfig[]; | ||||||
|  | 	calendars: CalendarConfig[]; | ||||||
| 	build_time: { | 	build_time: { | ||||||
| 		iso: string; | 		iso: string; | ||||||
| 		rfc2822: string; | 		rfc2822: string; | ||||||
| @@ -26,11 +31,33 @@ export interface Context { | |||||||
| } | } | ||||||
|  |  | ||||||
| export interface FrontMatter { | export interface FrontMatter { | ||||||
| 	title?: string; | 	skip?: boolean; | ||||||
| 	layout?: string; | 	layout?: string; | ||||||
|  | 	title?: string; | ||||||
|  | 	description?: string; | ||||||
|  | 	tags?: string[]; | ||||||
|  | 	author?: string | string[]; | ||||||
|  | 	rss?: RSSFrontmatter; | ||||||
|  | 	sitemap?: SitemapFrontmatter; | ||||||
|  | 	event?: EventFrontmatter; | ||||||
| 	[key: string]: unknown; | 	[key: string]: unknown; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | interface SitemapFrontmatter { | ||||||
|  | 	change_freq?: ChangeFreq; | ||||||
|  | 	priority?: number; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | interface EventFrontmatter { | ||||||
|  | 	start_time?: string; | ||||||
|  | 	end_time?: string; | ||||||
|  | 	time_zone?: `${string}/${string}`; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | interface RSSFrontmatter { | ||||||
|  | 	//  | ||||||
|  | } | ||||||
|  |  | ||||||
| export function render_template(template: string, context: Context, layout?: string, partials: Record<string, string> = { }, tags?: [ string, string ]) { | export function render_template(template: string, context: Context, layout?: string, partials: Record<string, string> = { }, tags?: [ string, string ]) { | ||||||
| 	partials['.content'] = template; | 	partials['.content'] = template; | ||||||
| 	return mustache_render(layout || template, context, partials, tags); | 	return mustache_render(layout || template, context, partials, tags); | ||||||
|   | |||||||
							
								
								
									
										54
									
								
								src/time.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/time.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  |  | ||||||
|  | import { DateTime } from 'luxon'; | ||||||
|  |  | ||||||
|  | export function now(zone?: string) { | ||||||
|  | 	return zone | ||||||
|  | 		? DateTime.now().setZone(zone) | ||||||
|  | 		: DateTime.now(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function from_iso(time: string, zone?: string) { | ||||||
|  | 	return zone | ||||||
|  | 		? DateTime.fromISO(time, { zone }) | ||||||
|  | 		: DateTime.fromISO(time); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // function date_formatters(lang: string, time_zone: string) { | ||||||
|  | // 	return { | ||||||
|  | // 		date() { | ||||||
|  | // 			return (text, render) => { | ||||||
|  | // 				return format_with_config(render(text), { | ||||||
|  | // 					dateStyle: 'short', | ||||||
|  | // 					timeZone: time_zone, | ||||||
|  | // 				}); | ||||||
|  | // 			}; | ||||||
|  | // 		}, | ||||||
|  | // 		time() { | ||||||
|  | // 			return (text, render) => { | ||||||
|  | // 				return format_with_config(render(text), { | ||||||
|  | // 					timeStyle: 'long', | ||||||
|  | // 					timeZone: time_zone, | ||||||
|  | // 				}); | ||||||
|  | // 			}; | ||||||
|  | // 		}, | ||||||
|  | // 		datetime() { | ||||||
|  | // 			return (text, render) => { | ||||||
|  | // 				return format_with_config(render(text), { | ||||||
|  | // 					dateStyle: 'short', | ||||||
|  | // 					timeStyle: 'long', | ||||||
|  | // 					timeZone: time_zone, | ||||||
|  | // 				}); | ||||||
|  | // 			}; | ||||||
|  | // 		}, | ||||||
|  | // 	}; | ||||||
|  |  | ||||||
|  | // 	function format_with_config(text: string, config: Intl.DateTimeFormatOptions) { | ||||||
|  | // 		const date = new Date(text.trim()); | ||||||
|  | // 		const formatter = new Intl.DateTimeFormat(lang, config); | ||||||
|  | // 		return formatter.format(date); | ||||||
|  | // 	} | ||||||
|  | // } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
		Reference in New Issue
	
	Block a user