in blog

Πως μετέτρεψα το WordPress blog μου σε static site με το Eleventy

Στο προηγούμενο άρθρο, κάναμε μια σύντομη εισαγωγή στο Jamstack και τους μοντέρνους static site generators. Σε αυτό, θα μοιραστώ μαζί σας τις σημειώσεις που κράτησα, από τη διαδικασία μετατροπής ενός παλιού μου blog, σε static site, με το Eleventy.

Τα βασικά του Eleventy

  • Αρχικά, ακολούθησα ένα βασικό tutorial για να το κάνω εγκατάσταση και να φτιάξω ένα βασικό project. Το documentation έχει αρκετά tutorials/ και κάνει ένα αξιοπρεπές intro στις βασικές λειτουργίες του framework, αν και σε κάποια σημεία θα σήκωνε λίγη βελτίωση (να είναι πιο λεπτομερές για τεχνικά θέματα). Στήνοντας το project του tutorial, κατάλαβα τα περισσότερα κομμάτια του πως δουλεύει ο generator.
  • Χρειάστηκε να δω τα βασικά του Nunjucks. Έχω δει παλαιότερα κάτι αντίστοιχο με μια templating γλώσσα στη Ruby όταν είχα φτιάξει ένα μικρό project με Ruby-on-Rails. Σίγουρα είναι ωραία εργαλεία για διαχωρισμό του front-end.

Το περιεχόμενο

Τα άρθρα

  • Aφού κατάλαβα τα βασικά, ήρθε η ώρα να σκεφτώ πως θα διαχειριστώ την μεταφορά του αρκετού content του WordPress. Μιλάμε για ένα blog με 100+ άρθρα, που έχει περάσει από blogger, μετατροπή σε WordPress και τώρα πρέπει να γίνει markdown για το Eleventy. Ευτυχώς, πριν από μερικούς μήνες είχα καθίσει και είχα καθαρίσει αρκετό από το περιεχόμενο, φτιάχνοντας σημεία με artifacts από παλιά imports και από τους παλιούς WordPress editors.

    • Στη χειρότερη, σκέφτηκα, θα χρειαζόταν να γράψω κάποιο δικό μου custom script για export και μετατροπή, αλλά είδα πως ήδη υπήρχαν κάποια έτοιμα, οπότε τεστάρω αν δουλεύουν για τις ανάγκες μου.
  • Κατέβασα ένα έτοιμο script για Node.js, που μετατρέπει τα WordPress Exports σε Markdown.

    • Το script είναι πολύ καλό. Φορτώνει ταυτόχρονα και όλες τις εικόνες των post και κάνει και ένα transformation στα url τους στο output, ώστε να λειτουργούν αμέσως. Πολύ βολικό!

Τα comments

  • Συνειδητοποίησα ότι έχω αρκετά comments, που θέλω να μείνουν στο περιεχόμενο. Οπότε, έκανα μια μετατροπή στο προηγούμενο script ώστε να κάνει parse και τα comments των WordPress posts, και να τα προσθέτει στο front-matter. Θεώρησα ότι το front-matter είναι το πιο απλό βολικό σημείο, αφού κρατάει όλα τα δεδομένα μαζεμένα και encapsulated στο post τους. Εξάλλου, δεν θα προστεθούν ποτέ άλλα.

Cleaning-up

  • Μετά, είχα θέματα με το ότι κάποια comments ήταν encoded με παράξενους χαρακτήρες. Έβαλα στο προηγούμενο script έναν html decoder (ένα npm package) για να το φτιάξω.
  • Επειδή το output των comment πήγαινε αυτούσιο στο frontmatter του Eleventy (που είναι σε Yaml), έπρεπε να κάνω escape τα quotes, αφού χαλούσαν τον parser του Eleventy και έβγαζε error.
    • Αυτό ήταν το πιο debugging κομμάτι, αφού έπρεπε να βρω edge-cases από τόσα χρόνια κειμένων που "βαρούσαν άσχημα" στο YAML. Συγκεκριμένα, σε κάποια σχόλια είχα χρησιμοποιήσει πολλές παύλες για να δώσω έναν διαχωρισμό μεταξύ sections (——–). Αυτό χτυπούσε άσχημα στο YAML, αφού οι τρεις παύλες οριοθετούν το κομμάτι του front-matter. Απλούστατα, εδώ ήταν θέμα έρευνας και manual ελέγχου του build. Τελικά, τις αφαίρεσα όλες με ένα regex.

Permalinks

  • Το επόμενό μου θέμα ήταν τα permalinks. Κατ’ αρχήν, ήθελα να κρατήσω ακριβώς την ίδια μορφή permalinks με πριν, για λόγους SEO. Εξάλλου, ήταν όπως μου άρεσαν: {Διεύθυνση Site}/{Όνομα Άρθρου}. Το Eleventy, δυστυχώς, είναι αρκετά opinionated για το πως ένα folder αντικατοπτρίζεται στο output. Εγώ ήθελα να έχω τα post σε έναν ξεχωριστό φάκελο του source, αλλά έτσι δεν θα είχα τα καθαρά permalinks που θέλω. Από την άλλη, μπορούσα να δηλώσω συγκεκριμένο permalink σε κάθε ένα άρθρο ξεχωριστά, αλλά ήθελα να το κάνω με μια αυτοματοποιημένη διαδικασία, γιατί ήταν πολλά.
    • Τελικά, χρησιμοποίησα ένα data file με javascript στον φάκελο των Articles, ώστε να αλλάξω δυναμικά όλα τους τα permalinks με βάση το slug τους (το όνομα του φακέλου τους). Όλα τα αρχεία του φακέλου, χρησιμοποιούν το data file σαν κομμάτι του build τους, και θέτουν το σωστό permalink.

Αυτός είναι ο κώδικας:

Αρχείο: articles.11tydata.js

module.exports = {
  eleventyComputed: {
    permalink: data => (data.page.fileSlug + "/"),
    excerpt: data => data.excerpt,
  }
};
  • Επειδή το παραπάνω, δημιουργούσε πρόβλημα με το pagination, αφού άλλαζε τη διεύθυνση των paginated σελίδων, οι archive σελίδες με pagination αποτέλεσαν ειδική περίπτωση. Εκεί, χρησιμοποίησα δεδομένα για το pagination στο permalink της σελίδας, που υπολόγιζα στο ίδιο το front matter του. Εξάλλου, στο data cascade, το front-matter έχει πάντα προτεραιότητα:
permalink: "articles/{% if pagination.pageNumber > 0 %}{{pagination.pageNumber+1}}/{% endif %}"
  • Το έκανα να μην δείχνει νούμερο για την πρώτη σελίδα, και να δείχνει για όλες τις υπόλοιπες.
    • Προσοχή με τα κενά στο templating, γιατί όλα μπαίνουν στο html και μπορεί να οδηγήσουν σε περίεργα bugs.

Design και CSS

  • Μετά δούλεψα στο CSS. Είχα κάνει ένα απλό design στο Figma, ώστε να ξέρω τι θέλω να φτιάξω, αλλά σίγουρα ήμουν ανοιχτός στο να δω τι δουλεύει. Πάντως, ο σκοπός μου ήταν να φτιάξω κάτι απλό, readable και content-centric, που σε γενικές γραμμές έμοιαζε με την προηγούμενη μορφή του blog.
    • Αφού προσπάθησα με μερικά έτοιμα CSS frameworks (όπως το Bulma), αποφάσισα ότι είναι αρκετά περίπλοκα για αυτό που θέλω να κάνω. Οπότε, αποφάσισα να γράψω απλό, "χειροποίητο" CSS. Και στην τελική, θα αποκτούσα και γνώση που θα μου έμενε, ανεξαρτήτως framework.
    • Είχα, πάντως, αρκετό καιρό να ασχοληθώ τόσο έντονα με CSS για layouts, οπότε σίγουρα μου πήρε και λίγο χρόνο το να διαβάσω και να μάθω για τα μοντέρνα συστήματα layout, όπως το flexbox και το css grid.

Τεστάροντας

  • Μετά, έκανα import όλο το ιστορικό των post, για να δω αν όλα δουλεύουν.
  • Επειδή το Eleventy δεν έχει (προς το παρόν) επιλογή για passthrough copy των εικόνων relative σε κάθε ξεχωριστό post, χρησιμοποίησα ένα plugin που έκανε ακριβώς αυτό. (Το plugin νομίζω πως έκανε και διόρθωση των url για να δείχνουν σωστά.)
    • Είχε όμως μείνει το παλιό link των εικόνων, στη διεύθυνση των wordpress assets του παλιού blog.
    • Όλο αυτό αργότερα άλλαξε, αλλά προς το παρόν ας μείνουμε σε αυτό…

Λεπτομέρειες

Regex για link στις εικόνες που έμειναν προς το παλιό site

  • Μέσα από τη μεταφορά των WordPress post, είχαν απομείνει κάποια κατάλοιπα, που θεώρησα ότι ήταν στο κατάλληλο μέγεθος για να τα αντιμετωπίσω σχεδόν manually.
    • Από εδώ και πέρα, τα Regex ήταν ένα από τα βασικά μου εργαλεία.
    • Με αυτό το Regex βρήκα όσα links προς εικόνες είχαν μείνει (για κάποιον λόγο) να δείχνουν προς το παλιό site και τα άλλαξα:
((http\[s\]{0,1}://foithtips.com/wordpress/wp-content/uploads/)\[\\S\]*((.png)|(.jpg)|(.jpeg)|(.gif)))

Λειτουργίες που έφτιαξα για το Eleventy

Ως σχετικά καινούργιο framework, το Eleventy δεν έχει built-in πολλές από τις λειτουργίες που στο WordPress θεωρούμε δεδομένες. Από την άλλη, είναι πολύ ευέλικτο και τα περισσότερα πράγματα είναι εύκολο να φτιαχτούν με λίγο κώδικα.

Έτσι κι εγώ, αντί να ψάχνω στα τυφλά plugins όπως παλαιότερα στο WordPress, έπρεπε "να λερώσω λίγο τα χέρια μου" και να φτιάξω κάποιες λειτουργίες από μόνος μου.

Related Posts

  • Προσπαθώντας να φτιάξω related posts, πήγα να χρησιμοποιήσω έτοιμο κώδικα, αλλά με προβλημάτισε περισσότερο γιατί το Eleventy είναι τόσο extensible που καμιά φορά μπορεί να χρησιμοποιείται πολύ διαφορετικός συνδυασμός templates από την μια περίπτωση στην άλλη.
  • Οπότε, κατέληξα να γράψω τον δικό μου κώδικα, με τον δικό μου κατανοητό τρόπο, έχοντας πρώτα δει τις ιδέες άλλων developers και έχοντας διαβάσει τον κώδικά τους.
  • Τελικά, έφαγα μια ολόκληρη μέρα να κάνω debugging μια μ^$%^α στον custom κώδικά μου γιατί πολύ απλά η Javascript είναι απαίσια, δεν δείχνει που ακριβώς είναι το πρόβλημα και γενικά είναι σιχαμένη, όπως κάθε dynamically-typed γλώσσα…
    • Το πρόβλημά μου ήταν με τα undefined, που χτυπούσαν όταν πήγαινα να προσπελάσω κάποιο property από object. Δυστυχώς, το Eleventy δεν είχε τόσο πλήρες documentation στο θέμα του τι περιέχει κάθε object που παράγει, οπότε ήταν ένα συνεχές debugging για εμένα, ώστε να καταλάβω που κρύβεται η κάθε πληροφορία.
  • Πάντως είμαι περήφανος για αυτό το κομμάτι, αφού δουλεύει τέλεια. Η ιδέα είναι απλούστατη: Για κάθε post ελέγχω πόσα κοινά tags έχει με κάθε άλλο. Μετράω κάθε ταίριασμα σε ένα hashmap, και στο τέλος κάνω sort και προβάλλω πχ. τα 5 πρώτα ως related.
  • Ο αλγόριθμος είναι O(n^2) και τρέχει από την αρχή σε κάθε build (δεν υλοποίησα κάποιο caching), αλλά για το μέγεθος του blog μου δεν παίζει ρόλο, αφού το Eleventy είναι πολύ γρήγορο και κάνει build σε millisecond.
  • Δεν το υλοποίησα ως τυπικό plugin του Eleventy, αλλά ως ένα data file (στον _data φάκελο) που τρέχει σε κάθε build και μετά παρέχει τις πληροφορίες σε κάθε post.
module.exports = {
    site_name: "relatedPosts",
    getRelated: (currentTitle, currentTags, posts, postLimit) => {

        // [! This is an object with key/values ]
        let postsWithSimilarTags = new Map();

        // [! Posts is array ]
        if (posts !== undefined) {
            for (post of posts) {
                if ((post !== undefined) && (post.data !== undefined) && (post.data.title !== undefined)) {

                    // [! This is an object with key/values ]
                    let similarTags = 0;

                    if (post.data.tags !== undefined) {
                        for (tag of post.data.tags) {
                            if (currentTags !== undefined) {
                                if ((tag !== undefined) && currentTags.includes(tag)) {
                                    similarTags++;
                                }
                            }
                        }
                    }

                    if ((similarTags > 0) && (currentTitle !== post.data.title)) {
                        postsWithSimilarTags.set(post, similarTags);
                    }
                }
            }
        }

        // Now, we transform the hashtable to an array:
        let sortableArray = [];
        postsWithSimilarTags.forEach((value, key) => {
            sortableArray.push({
                post: key,
                similarity: value
            });
        });

        //Now we'll order the array, according to their similarity:
        let sorted = sortableArray.sort(function(a, b) {
            if (a.similarity < b.similarity) {
                return 1;
            }
            if (a.similarity > b.similarity) {
                return -1;
            }
            return 0;
        });

        // Returns an array of {post, similarity} objects
        // The "post" object contains post.url, or the post.data... parameter that
        // contains all front-matter data:
        return sorted.slice(0, postLimit);
    }
}

Excerpts and Permalinks

  • Στη συνέχεια, ήθελα να κάνω καλύτερη τη σελίδα του αρχείου των post. Ήθελα η λίστα να φαίνεται καλύτερη αισθητικά και να υπάρχει ένα δυναμικό excerpt (αφού δεν είχα καμία όρεξη να γράψω manually excerpts για 100+ post)
  • Προσπάθησα ξανά με ένα javascript data-file στον φάκελο των posts, που κάνει override το frontmatter computation, θέτει permalinks και μετά υπολογίζει ένα excerpt.
  • Τελικά, όμως, δεν δούλεψε όπως ήθελα, γιατί δεν έχω πρόσβαση (με απλό τρόπο) στο content του κάθε post μέσα από το data file και ήταν πολύ hacky λύση. (Μετά, βρήκα τον τρόπο, αλλά ήταν αργά..)
  • Οπότε αποφάσισα να το κάνω μέσα στο ίδιο το template με χρήση του Nunjucks
    • Αυτό ήταν πολύ πιο απλό! Κάνοντας λάθη, μάθαινα συνεχώς ποιός είναι ο "τρόπος" του Eleventy και όσο μπορούσα δεν του πήγαινα κόντρα.
    • Όλα τα filters του παρακάτω κώδικα, είναι built-in στο Nunjucks.
<p class="excerpt">
    {% if post.data.excerpt !== "" %}
        {# If an excerpt exists for this post, show it #}
        {{post.data.excerpt}}
    {% else %}
        {# Otherwise, create an excerpt from the content #}
        {{ post.data.content | striptags | truncate(300) | safe }}
    {% endif %}
</p>

Προβλήματα με το documentation

  • Εντάξει, για καινούργιο open-source project, το documentation δεν είναι τόσο κακό, αλλά κατά τη γνώμη μου του λείπουν αρκετά τεχνικά κομμάτια, όπως εξήγηση του πως δουλεύει το backend σύστημα και του τι περιέχουν τα παραγόμενα objects
  • Για παράδειγμα, σε ένα template που χρησιμοποιούσα pagination (page.njk, postArchive.md, pagelist.njk), το page.data.content ήταν άλλο στην 0 σελίδα και άλλο στην 1η, και αυτό μου χαλούσε τα αυτόματα excerpts που έφτιαχνα.
  • Μετά από ώρα debugging, κατάλαβα ότι ήθελα το post.templateContent αντί το post.data.content που χρησιμοποιούσα, αλλά αυτό δεν το έλεγε πουθενά.

Η δομή του post (object) είναι η εξής

post:
- date
- outputPath (ο φάκελος που πηγαίνει στο build)
- url
- templateContent \[Getter/Setter\](το input, αλλά με html tags)
- template .... (configs για τα templates εσωτερικά του Eleventy)
- frontMatter (αυτό είναι πριβέ κομμάτι - Παρακάτω υπάρχει το σωστό)
    - content (το καθαρό markdown του input)
    - data...
    - υπόλοιπα frontmatter κλειδιά...
    - computed data...
- inputPath
- fileSlug
- data
    - public data from _data folder are accessible here
    - Όλα τα προσβάσιμα στοιχεία του frontmatter
    - tags
    - comments
    - page
        - date
        - inputPath
        - fileSlug
        - filePathStem
        - url
        - outputPath
    - collections...
    - permalink
    - content (το τελικό content του page)
    - layoutContent

Βάζοντας ένδειξη σελίδας (σε ποιά σελίδα του pagination βρισκόμαστε) στη λίστα άρθρων

  • Πήγα να βάλω Nunjucks μέσα στο content του markdown, αλλά δεν δούλευε. Τελικά, έβαλα Liquid και δούλεψε. Μπορεί οι δύο templating γλώσσες να μοιάζουν, αλλά πρέπει πάντα να είσαι σίγουρος ότι σε κάθε σημείο χρησιμοποιείς αυτή που πρέπει (ή που έχεις δηλώσει στο config).

Το hostname

  • Είδα πως το να έχω relative urls δεν είναι η καλύτερη πρακτική (ή και να είναι, αλλά τέλος πάντων, ήθελα να μπορώ να φτιάξω absolute urls για κάποια πράγματα). Οπότε έφτιαξα ένα data file (στον φάκελο _data που το Eleventy αναγνωρίζει ως global) όπου κρατάω το hostname και το έβαλα ως tag στα templates μου, κάνοντας ένα regex-replace στο παλιό μου περιεχόμενο.
    • Με τέτοια centralized config files, καταφέρνουμε να μην επαναλαμβάνουμε κώδικα (Don’t Repeat Yourself principle)
    • Για regex, χρησιμοποιούσα το Notepad++ που πραγματικά είναι πολυεργαλείο.
    • Για να στήσω (και να τεστάρω) τα Regex μου, χρησιμοποιούσα το Regexr.

Το δεύτερο cleanup

  • Παρά τα regex και τα scriptάκια, στο τέλος-τέλος το περιεχόμενο ήθελε ακόμη ένα manual πέρασμα. Η χειρονακτική δουλειά είναι σχεδόν σίγουρη σε ένα τέτοιο εγχείρημα.

Έφτιαξα έναν categories selector για τα collections

  • Το Eleventy φτιάχνει αυτόματα collections ανάλογα με τα tags, αλλά εγώ ήθελα και την έννοια των collections, ώστε να μπορώ να διαλέγω λίστες από post με κατηγορίες, εύκολα όπως τα tags.
  • Με αυτό τον κώδικα, έγινε και αυτό:
    // Create collections of categories:
    let makeCategories = function (collection) {

        let categories = {};

        collection.getAllSorted().forEach(item => {
            let category = item.data.categories;

            if (category !== undefined) {
                if (Array.isArray(categories[category])) {
                    categories[category].push(item);
                } else {
                    categories[category] = [item];
                }
            }
        });

        return categories;
    }

    eleventyConfig.addCollection("categories", makeCategories);

Μεταφέροντας όλες τις εικόνες σε έναν φάκελο

  • Επειδή τελικά το plugin που χρησιμοποίησα για να βάζει τις εικόνες στους φακέλους του κάθε post, δημιουργούσε προβλήματα αν ήθελα ταυτόχρονα να χρησιμοποιήσω και έναν φάκελο ‘images/’, αποφάσισα να μεταφέρω όλες τις εικόνες των post στον φάκελο με κάποιο bash script.
  • Κατ’ αρχήν, έβαλα το Linux Subsystem for Windows που είναι απλά μαγικό, αφού τρέχω Bash στα Windows!
  • Με αυτό το script, μετέφερα αυτόματα όλες τις εικόνες εκεί που ήθελα: find . -name "*.jpg" -or -name "*.png" | xargs mv -t images/

Τα χρώματα

  • Δεν έδινα ακόμη μεγάλη σημασία στο design. Πάντως, χρησιμοποίησα τα css variables που είναι πολύ χρήσιμα για να αλλάξω εύκολα το color scheme αργότερα (ή να φτιάξω και dark mode).
    • Και πάλι "Don’t repeat yourself"
    • Θα μπορούσα να χρησιμοποιήσω εδώ και κάποιον CSS preprocessor (όπως SASS) αλλά ήθελα να μείνω στην απλότητα των εργαλείων για αυτό το project.

Filter για εισαγωγή περιεχομένου στα μισά του post

  • Ήθελα να μπορώ να εισάγω περιεχόμενο, όπως widgets ή banners, στα μισά ενός post. Η ιδέα ήταν να μετράω τα paragraph tags και να εισάγω κείμενο ακριβώς πριν το μεσαίο.
  • Ασχολήθηκα πολύ με αυτό, κυρίως με regex για να βρίσκω το Nth substring ενός post, αλλά τελικά δεν δούλευε και αποφάσισα να μην φάω χρόνο σε αυτό και να το κάνω απλούστερα, βάζοντας απλά όποιο widget ήθελα στο τέλος του post.
    • Όχι πως δεν γίνεται, πάντως! Απλά ήμουν αρκετά κουρασμένος εκείνη τη στιγμή για να θέλω να ασχοληθώ…

Going live

  • Πάντα, σε ότι έχω προσπαθήσει να φτιάξω, τα πράγματα παίρνουν πολλαπλάσιο χρόνο από το αναμενόμενο.
  • Είπα να κάνω ship το site για να τελειώνω. Ο host μου, όμως, είναι υπερ-αργός και εκεί που πήγα να κάνω ένα backup το παλιό blog μέσω Plesk (καλού-κακού), το site άρχισε να βαράει 502 error, αφού ο server κόλλησε.
  • Όταν τελείωσε, έσβησα το παλιό WordPress και έβαλα το static. Αμέσως, σχεδόν μαγικά, δούλεψε.
    1. Και ήταν υπερ-γρήγορο.

Aftermath

  • Θυμήσου: Η δουλειά ΠΟΤΕ δεν τελειώνει. Πάντα υπάρχουν μικρά λαθάκια και bugs, σε κάθε project, μικρό ή μεγάλο.

Περισσότερες υλοποιήσεις και διορθώσεις

Δυναμικά descriptions

  • Μετά, είπα να κοιτάξω χαλαρά το site, και γρήγορα είδα ότι τα open graph tags δεν δουλεύουν. Λογικό, αφού δεν τα είχα υλοποιήσει.
  • Tώρα το site περιείχε όλα τα meta tags που χρειάζεται για SEO και Open Graph. Τα περισσότερα άρθρα μου, όμως, δεν είχαν έτοιμο description.
  • Οπότε, κάθισα και έφτιαξα ένα αυτόματο σύστημα για να φτιάχνει δυναμικά descriptions και να βρίσκει τις πρώτες εικόνες για cover image.
  • Η υλοποίηση ήταν παρόμοια με αυτή των excerpts, απλά την έτρεχα στο "head" partial layout που έχω, με μόνο hack το ότι για να έχω πρόσβαση στο content (αφού το Eleventy δεν μου το παρέχει στο page object) έψαχνα το τρέχων post στο collections.posts, συγκρίνοντας με το page.url. Έτσι, κατάφερνα να έχω πρόσβαση στο content και να πάρω το κομμάτι που ήθελα για το description.

Wordcounter problem

  • Σε κάθε post έχω έναν counter, που γράφει τον αριθμό λέξεων του άρθρου και τον εκτιμώμενο χρόνο ανάγνωσης. Τεστάροντας το site, συνειδητοποίησα ότι έπεφτε πολύ έξω.
  • Τελικά wordcounter του Nunjucks δεν δουλεύει καλά με Ελληνικούς χαρακτήρες. Γι’ αυτό έφτιαξα ένα custom δικό μου φίλτρο που δουλεύει σωστά.

Αποτέλεσμα

  • Το site είναι live στο https://foithtips.com
  • Όλα δουλεύουν τέλεια, αφού είναι απλά στατικές html σελίδες
  • Το site είναι άρρωστα γρήγορο και βαράει κάτι 97άρια στα speed tests της Google (πριν ήταν γύρω στο 30 για mobile)

Next Steps?

Build automations

Η κοινή λογική για έναν static site generator, είναι να έχεις το source στο Git, και να κάνει αυτόματα build και deploy κάθε φορά που pushάρεις σε ένα συγκεκριμένο branch. Αυτό, φυσικά, είναι πολύ βολικό για ένα site που ανανεώνεται συχνά, και πιθανότερα θα το στήσω όταν μετατρέψω και το προσωπικό μου blog.

Προς το παρόν, όμως, απλώς έκανα copy-paste τα αρχεία στον server μου με το χέρι και όλα είναι καλά. Εξάλλου, η πιθανότητα να αλλάξω κάτι στο site τον επόμενο καιρό, είναι πολύ μικρή. Και αν χρειαστεί, ο πηγαίος κώδικας είναι ήδη στο Git, οπότε μπορώ να το κάνω build από παντού.


To site που έφτιαξα με το Eleventy βρίσκεται live στο https://foithtips.com

Η Checklist είναι ένα δι-εβδομαδιαίο newsletter για την παραγωγικότητα, τους στόχους, τη ζωή στο εξωτερικό, τη διαχείριση του χρόνου και ότι επηρεάζει τη ζωή μας.

Γίνε μέλος μαζί με 600+ αναγνώστες για να λαμβάνεις την Checklist στο email σου.

Η κάνε κλικ εδώ για να μάθεις περισσότερα.

Comment: