"Make" का उपयोग करके static site generator (2022)
(karl.berlin)- Static site को host करना आसान और तेज़ होता है, और maintenance भी कम चाहिए; इसलिए अगर साइट सरल है तो अलग builder सीखे बिना Makefile की कुछ पंक्तियों से ही काम चल सकता है
- मुख्य संरचना यह है कि
sourceमें मौजूद file layout को बनाए रखते हुए HTML मेंheader.htmlजोड़ा जाए, और बाकी files को जैसा है वैसा copy करकेbuildमें रखा जाए make buildमेंfind source -type fऔरpatsubstसे input और output files को match किया जाता है, और.htmlतथा अन्य files पर अलग-अलग make rules लागू होते हैं- current page highlight करना, Markdown conversion, local server, change detection पर rebuild, और GitHub Pages deployment जैसी चीज़ें ज़रूरत पड़ने पर additional targets के रूप में जोड़ी जा सकती हैं
- Dependencies कम होने और बदलने की जगह साफ़ दिखने की वजह से, छोटे requirements वाली sites में generic generator की तुलना में खुद बनाया गया build flow ज़्यादा तेज़ हो सकता है
सिर्फ Makefile से बनने वाली basic static site
- Static site generator का output host करना आसान होता है, तेज़ चलता है, और maintenance का बोझ बहुत कम होता है
- अगर साइट सरल है, तो existing site builder को सीखने और उसके हिसाब से ढलने के बजाय खुद script लिखना ज़्यादा तेज़ और संतोषजनक हो सकता है
- अगर यह auto-updating timestamp या RSS feed के बिना एक साधारण site है, तो blog scripts से भी ज़्यादा सरल setup काफ़ी है
-
बुनियादी आवश्यकताएँ
- सभी input files
sourcedirectory में रखी जाती हैं, और output में भी वही directory structure बनाए रखा जाता है - सभी HTML files की शुरुआत में
header.htmlजोड़ा जाता है - HTML के अलावा बाकी files को बिना बदलाव
builddirectory में copy किया जाता है
- सभी input files
-
Makefile rules
buildtarget की dependency ऐसी होती है किsourceके नीचे मौजूद सभी files,buildके नीचे corresponding output files से map हो जाएँ- इसका अपना कोई काम नहीं होता, लेकिन यह entry point की तरह काम करता है ताकि हर file पर सही rule लागू हो
build/%.htmlrule,source/%.html,header.html,Makefileपर depend करता हैmkdir -p $(dir $@)से output directory बनाई जाती है, औरcat header.html $< > $@से header और input HTML को जोड़कर output बनाया जाता हैbuild/%rule, HTML के अलावा बाकी files कोcp $< $@से जैसा है वैसा copy करता है- सिर्फ इस
header.htmlऔर इन rules सेmake buildचलाने पर ऐसाbuilddirectory बन जाता है जिसे local में खोला जा सकता है या web server पर upload किया जा सकता है
विस्तार के उदाहरण और helper targets
-
current page दिखाना
- Navigation में current page को highlight करने से visitor एक नज़र में समझ सकता है कि वह साइट में कहाँ है
- उदाहरण में navigation link को ढूँढकर उसमें
currentclass जोड़ी जाती है sed -E 's|(href="$(subst source,,$<))|class="current" \\1|' header.html | cat - $< > $@- असल replacement तरीका इस्तेमाल किए जा रहे markup structure के अनुसार बदलना चाहिए
-
Markdown से HTML बनाना
- अगर आप सीधे HTML नहीं लिखना चाहते, या existing content Markdown format में है, तो Markdown-to-HTML converter को pipe में जोड़ा जा सकता है
- उदाहरण में smu का उपयोग किया गया है
smu $< | cat header.html - > $@- Basic rule अब भी यह मानता है कि
build/foo.html,source/foo.htmlसे बनता है - Markdown files के लिए भी
.htmlsuffix बनाए रखना होगा, या input के रूप में.mdfiles खोजने के लिए rule बदलना होगा
-
local preview
- कुछ sites में browser में local files सीधे खोलने पर preview सही नहीं दिखता, और इसका आम कारण relative links की जगह absolute links का इस्तेमाल होना है
- Python कई systems में पहले से installed होता है, और इस काम के लिए उपयुक्त web server भी साथ लाता है
servetarget:python -m http.server -d build
-
बदलाव पर automatic rebuild
- साइट पर काम करते समय हर बार manually फिर से build करना झंझट भरा होता है
entrका उपयोग करने परsource,header.html,Makefileमें बदलाव होने परmake buildअपने-आप चलाया जा सकता हैwatchtarget:find source header.html Makefile | entr make build- अगर dependency से बचना हो, तो
inotifywaitका भी उपयोग किया जा सकता है
-
GitHub Pages deployment
- अगर आपका repository GitHub पर है, तो generated HTML को GitHub Pages पर host करना स्वाभाविक विकल्प है
- git की details से बचे रहते हुए deployment command को सही तरह सेट करना थोड़ा पेचीदा हो सकता है, लेकिन
git worktreeसे यह किया जा सकता है - यह तरीका Sangsoo Nam की post पर आधारित है
gh-pagesbranch कोpublic_htmlworktree के रूप में जोड़ा जाता हैbuild/*कोpublic_htmlमें copy किया जाता हैgit add --all,git commit -m "Deploy to github pages",git push origin gh-pagesचलाए जाते हैं- अंत में
git worktree remove public_htmlसे worktree हटा दी जाती है
-
वास्तविक उदाहरण
- इस approach से बनी page karlb/astridbartel.de में देखी जा सकती है
- पूरा static site generator Makefile की छह पंक्तियों से शुरू किया जा सकता है, और बिना किसी अजीब dependency या maintenance बोझ के ज़रूरत के हिसाब से तेज़ी से बदला जा सकता है
1 टिप्पणियां
Hacker News की रायें
बाद में इसमें news और RSS feeds, research papers और lecture materials की automatic listing, tags से filter की जा सकने वाली book list जैसी सुविधाएँ जोड़ीं। आज भी यह Makefile ही है, लेकिन Makefile खुद उल्टा थोड़ा सरल हो गया है; इसकी जगह HTML को line-by-line संभालने के लिए बेहतरीन xml2 और 2xml utilities इस्तेमाल करने वाली Bash scripts call करता है। मुख्य रूप से
grepऔरsedजैसी core utilities का इस्तेमाल करता हैइसके साथ कुछ git hooks भी लगाए हैं जो जरूरत पड़ने पर अपने-आप
makecall करते हैं, खासकर जिस remote server पर वेबसाइट host होती है, वहाँ repository में push करने पर public version फिर से build हो जाता हैकई सालों से बहुत अच्छी तरह चल रहा है, और git history 2009 तक जाती है। शुरुआती commits देखकर पता चला कि वे
beccad7 (FIRST_VERSION) Initial commit,d1cc6d7 adding link to Google Reader shared items,6ccfd0c fix typo,d337959 adding link to Identi.ca accountथे, और सचमुच 15 साल बीत चुके हैंxml2और2xmlutilities कुछ छोड़े हुए project जैसी लगती हैंhttps://github.com/cryptorick/xml2
https://manpages.debian.org/unstable/xml2/2xml.1.en.html
source/से file delete करने पर भी वहbuild/से delete नहीं होतीमेरे projects में पूरा site फिर से build करना भी पर्याप्त तेज है, इसलिए मैंने rebuild से पहले पूरे build folder को delete करने का रास्ता चुना
https://github.com/jez/jez.github.io/blob/source/Makefile#L1...
इससे build system इस्तेमाल करने की बड़ी वजह, यानी incremental builds, काफी हद तक निष्प्रभावी हो जाती है, लेकिन फिर भी अगर आपको पता है कि कौन-सा page फिर से generate करना है, तो उस file को सीधे
makeकर सकते हैंMakefile में इस pattern के लिए कोई common workaround हो तो जानना चाहूँगा
GNU Make के
shellfunction से files list कीं, औरfilter-outfunction से “expected” outputs को filter कर दिया। यह देखने में भद्दा hack है, लेकिनshellfunction के जरिए variable expansion के हिस्से के रूप में command चलवाकर यह सुनिश्चित किया कि वह हर बार चलेMakefile link: https://github.com/jaredkrinke/make-blog/blob/main/Makefile
make cleanजैसे nuclear option के अलावा, deletion और renaming के लिए specific Make targets रखे जा सकते हैं। उदाहरण के लिएmake rm sourcefileयाmake mv sourcefile newsourcefileको source और generated target, दोनों के delete/rename को साथ में handle करने जैसा बनाया जा सकता हैअसल में काफी बड़े blogs या online projects में भी
make clean/make allcycle आम तौर पर काफी तेज होती है, और templates या site design elements ठीक करते समय अक्सर जरूरत पड़ती है। अगर rebuild time चिंता करने लायक scale का हो, तो शायद source को database में manage करके client access पर dynamically generate करने वाला असली CMS इस्तेमाल करना सही हो सकता हैmake cleanसे काम नहीं चलेगा?rm/%.html:@rm -f source/%.html build/%.htmlइसे
$ make rm/page.htmlसे चलाएँprunefeature हैhttp://neilmitchell.blogspot.com/2015/04/cleaning-stale-file...
लेकिन
makeमें भी चलने वाला सबसे अच्छा समाधान मुझे यह लगता है कि final.tar.gzarchive बनाने के लिएmake disttarget रखा जाए। Rules सही लिखे हों तो पुराने files उसमें शामिल नहीं होंगे। बड़े projects में यह slow हो सकता है, लेकिन development के दौरान वैसे भी इसका इस्तेमाल करने की जरूरत नहीं, सिर्फ release के समय करना होगा। Incremental build अब भी possible है, और सिर्फ आखिरी.tar.gzको scratch से बनाना होगाblog.shshell script काम से मुझे सीधे inspiration मिलीउसे लेकर modify करके मैंने अपना minimal static site generator
barfबनाया। Karl ने अपना शानदार काम public न किया होता तो यह मौजूद नहीं होता[0]: https://github.com/karlb/karl.berlin/blob/master/blog.sh
[1]: https://barf.bt.ht
shiteकहता हूँ, और इसी से अपनी site बनाता हूँ। नाम software quality की ओर इशारा करता है :)सबसे अच्छी बात यह है कि अब तक कुछ भी upgrade करने की जरूरत नहीं पड़ी, और आगे भी शायद नहीं पड़ेगी। दूसरी सबसे अच्छी बात यह है कि JavaScript के बिना hot reload मिलता है
[1] https://github.com/adityaathalye/shite
[2] https://evalapply.org
m4को थोड़ा मिलाने से उसी bare-bones approach को बनाए रखते हुए थोड़ी और flexibility मिल सकती हैलगभग 20 साल पहले मैंने इसी तरीके से बनी एक छोटी वेबसाइट maintain की थी। लेकिन personal websites को छोड़ दें तो आजकल यह मॉडल कितना फिट बैठेगा, पता नहीं। क्योंकि यह तरीका मूल रूप से Web 1.0 वाली भूमिकाएँ थोप देता है। योगदान देने वाले हर user को HTML में निपुण होना पड़ेगा, या फिर किसी को “webmaster” जैसा झंझट वाला काम अपने ऊपर लेना पड़ेगा
[1] https://en.wikipedia.org/wiki/M4_(computer_language)
m4” जैसी कोई चीज़ नहीं होतीएक clean project शुरू करते हैं और कसम खाते हैं कि इस बार
m4को हाथ नहीं लगाएंगे। फिर repetitive code कम करने के लिए एक छोटा-साm4call डाल देते हैंएक साल बाद वेबसाइट से “cat” शब्द चुपचाप गायब क्यों हो रहा है, यह खोजते हुए macro expansion की 5 परतें खंगालते हैं, और अंत में पता चलता है कि एक junior developer ने manual से copy करने के बजाय खुद for loop implement किया और quotes खराब कर दिए
तत्काल समस्या हल करने के बाद लगता है कि DSL debugging बहुत कठिन है, इसलिए वह M4 macro file उठा लाते हैं जिसे हर project में copy करते रहे हैं। इसके बाद output में comments जोड़कर stack trace generation script चलाने वाले macro-generating macro से
defineके सभी use बदलने में पूरा दिन निकल जाता हैअगले project में पक्का नियम बनाएंगे।
m4banned! हाँ, शायद उस एक जगह को exception रखा जा सकता हैm4याperlजैसे general text substitution पर निर्भर रहने के बजाय, HTML और XML का आधार और common superset SGML इस्तेमाल करने की सलाह दूँगाSGML आसान type-checked text macros, यानी entity expansion, built-in देता है, और type-aware parameterized macro expansion भी संभव है। यहाँ type से मतलब markup element के general content type से है, यानी कौन से child elements और किस order में allowed हैं; साथ ही attributes, CDATA, RCDATA जैसे contexts में expand या escape करने पर भी विचार होता है
संभावित रूप से malicious user comments को सही तरह expand/escape करते हुए, उदाहरण के लिए inline-level markup allow करना और
scriptelements block करना—ऐसे user rules को ठीक से केवल SGML ही expand कर सकता है। Markdown या wiki syntax को HTML में expand करना, external/syndication HTML content लाना, RSS और navigation के लिए outline बनाना वगैरह भी संभव है। command line पर काफी complex static site preparation tasks के लिए यह अच्छी तरह फिट बैठता है[1]: https://sgmljs.net/docs/producing-html-tutorial/producing-ht...
[2]: https://sgmljs.net/docs/sgmlproc-manual.html
m4याsedfind-and-replace की जगहenvsubstआज़माना अच्छा रहेगायह Bash-style variable references, जैसे
$TITLE, को environment variable values से replace करने वाला program हैexport CURRENT="..."cat page.html | envsubtm4”, प्लीज़ बिल्कुल नहींm4एक esoteric programming language के तौर पर भी खास नहीं हैसिर्फ यह तथ्य कि वह sendmail में चलता था, किसी चीज़ को justify करने के लिए काफी नहीं है
यहाँ जब भी कोई दिलचस्प post पढ़ता हूँ, feed subscribe कर लेता हूँ। चाहे Wordpress site हो, Bear Blog हो, Micro.blog हो, Havenweb blog हो, या किसी की खुद बनाई site की feed हो—मैं उसे Hey Homepage के
Really Social Sitesmodule में जोड़ देता हूँआखिरकार मैं इस blog list को public करना चाहता हूँ, जैसे Kagi अपनी Small Web initiative में करता है। लेकिन quality जोड़नी हो तो curation ही key है, और curation के बारे में सोचते-सोचते किसी online magazine जैसी चीज़ शुरू करना स्वाभाविक लगने लगता है
लोग दूसरों के साथ share करने लायक, अच्छे अर्थ वाला “entitlement” कहाँ से पाते हैं? मुझे समझ नहीं आता कि वे क्यों मान लेते हैं कि दूसरे लोग इस बात में interested होंगे कि मैं किस पर काम कर रहा हूँ। कभी-कभी यह competition जैसा लगता है। जैसे “blog पर likes या exposure पाने के लिए सबसे cool चीज़ बनानी होगी”
collaboration बेशक अच्छी चीज़ है, और सब कुछ public होगा तभी संभव है। बस “यह cool लग रहा है इसलिए कर रहा हूँ” और “reaction पाने के लिए इसे दूसरों के साथ share करने की कोशिश कर रहा हूँ” के बीच की line समझ नहीं आती
उसने कहा था कि सिर्फ एक test file बदलने पर भी एक ही command से tests run करने और graphs generate करने सहित पूरा paper दोबारा generate किया जा सकता है
https://pages.github.com/
मेरा version JavaScript के बिना hot reload चाहता था, इसलिए थोड़ा over-engineered हो गया, और यह एक मजेदार yak shaving था
basic idea वही है। templates के लिए heredoc इस्तेमाल करता हूँ, plain text को HTML में बदलने वाला compiler use करता हूँ, मेरे case में
pandocहै। index generation के लिए intermediate CSV का इस्तेमाल करता हूँ, और front matter निकालने की एक उपयोगीsedtrick भी है। classic और अच्छा[1] https://github.com/karlb/karl.berlin/blob/master/blog.sh
[2] https://github.com/adityaathalye/shite
[3] मेरा तरीका: https://github.com/adityaathalye/shite/blob/master/bin/templ...
हालांकि इसमें थोड़ी सीमा है। मैं लेखों को namespace के हिसाब से व्यवस्थित करता हूं और URL में तारीख डालता हूं, लेकिन
makeइसे सीधे अच्छी तरह handle नहीं कर पाताmakeका फायदा यह है कि धीमे compiler से build होने वाले बड़े programs में छोटे बदलाव होने पर यह incremental rebuild को काफी तेज बना देता हैजिस काम के full rebuild में 40 मिनट लगते, वह लगभग 3 सेकंड में खत्म हो सकता है
अगर कोई static site common header और कुछ सौ HTML files को
catकरने भर से 1 सेकंड से कम में शुरू से generate हो जाती है, तो script के बजायmakeइस्तेमाल करने का कोई फायदा नहीं है। dependency bug की वजह से अधूरा build होने का जोखिम ही पैदा होता है.phonyके रूप में mark कर सकते हैंफिर भी
make buildऔरmake pushजैसा distinction रखा जा सकता हैदूसरे छोटे project में मैंने temporary bundler के रूप में एक बहुत छोटी shell script इस्तेमाल की थी। तरीका CSS और JS को HTML के अंदर insert करने का था, और लक्ष्य यह था कि unbuilt files को भी local पर serve किया जा सके