פיתוח אפליקציות אינטרנט באמצעות Yaoman ו-Polymer

משדרגים את אפליקציות האינטרנט באמצעות כלים מודרניים

Addy Osmani
Addy Osmani

מבוא

Gogoley’. כל מי שכותב אפליקציית אינטרנט יודע עד כמה חשוב לשמור על פרודוקטיביוּת. זה מאתגר כשצריך לדאוג למשימות מעייפות כמו מציאת התבנית הנכונה, הגדרת תהליך עבודה של פיתוח ובדיקה והקטנה ודחיסה של כל המקורות.

למרבה המזל, כלים מודרניים לשמירה על ממשק הקצה יכולים לעזור באוטומציה של רוב הבעיות האלה, ולהתמקד בכתיבת אפליקציה מהירה. במאמר הזה נסביר איך להשתמש ב-Yeoman, זרימת עבודה של כלים לאפליקציות אינטרנט, כדי לייעל יצירת אפליקציות באמצעות Polymer, ספרייה של פולימרים וסוכר לפיתוח אפליקציות באמצעות רכיבי אינטרנט.

Yeoman

נעים להכיר: יו, גראנט ובאוור

יאומן הוא גבר בכובע שיש לו שלושה כלים לשיפור הפרודוקטיביות:

  • yo הוא כלי פיגומים שמציע מערכת אקולוגית של פיגומים ספציפיים למסגרת, הנקראת מחוללים, שניתן להשתמש בהם לביצוע חלק מהמשימות המייגעות שהזכרתי קודם.
  • הקובץ grunt משמש ליצירה, לתצוגה מקדימה ולבדיקה של הפרויקט, בזכות משימות שאספו חברי הצוות של Jeoman ו-grunt-contrib.
  • bower משמש לניהול תלות, כך שכבר לא צריך להוריד ולנהל את הסקריפטים באופן ידני.

באמצעות פקודה או שתיים בלבד, Yaoman יכול לכתוב קוד סטנדרטי לאפליקציה שלכם (או חלקים נפרדים כמו דגמים), להדר את ה-Sass, למזער ולשרשר את ה-CSS, ה-JS, ה-HTML והתמונות שלכם ולהפעיל שרת אינטרנט פשוט בספרייה הנוכחית שלכם. הוא יכול גם להריץ בדיקות יחידה ולבצע פעולות נוספות.

אפשר להתקין גנרטורים מ-Node Packaged Modules (npm) ויש יותר מ-220 גנרטורים שזמינים עכשיו, ורבים מהם נכתבו על ידי קהילת הקוד הפתוח. גנרטורים פופולריים כוללים גנרטור זוויתי, generator-backbone ו-generator-ember.

דף הבית של יאומן

אחרי שמותקנת גרסה עדכנית של Node.js, נכנסים לטרמינל הקרוב ביותר ומריצים את הפקודה:

$ npm install -g yo

זהו! עכשיו יש לך את Yo, GRunt ו-Buwer, ואפשר להריץ אותם ישירות משורת הפקודה. זה הפלט של הפקודה yo:

התקנה של יאומן

גנרטור פולימרים

כפי שהזכרתי קודם, Polymer הוא ספרייה של polyfills וסוכר שמאפשרת להשתמש ברכיבי אינטרנט בדפדפנים מודרניים. הפרויקט מאפשר למפתחים לבנות אפליקציות באמצעות הפלטפורמה של המחר ולהודיע ל-W3C על מקומות שבהם ניתן לשפר עוד יותר את המפרט במהלך הטיסה.

דף הבית של מחולל פולימרים

gener-polymer הוא מחולל חדש שעוזר לגבש אפליקציות של פולימרים באמצעות Yoman, ומאפשר ליצור ולהתאים אישית רכיבי פולימר (בהתאמה אישית) בקלות באמצעות שורת הפקודה, ולייבא אותם באמצעות ייבוא HTML. כך תוכלו לכתוב קוד סטנדרטי (בוילרפלייט) ולחסוך לכם זמן.

בשלב הבא, מתקינים את המחולל של Polymer באמצעות:

$ npm install generator-polymer -g

זה הכול. עכשיו יש לאפליקציה שלך כוחות על של רכיבי אינטרנט!

המחולל החדש שהותקן כולל כמה ביטים ספציפיים שתהיה לכם גישה אליהם:

  • polymer:element משמש כדי לגבש אלמנטים של פולימר נפרדים. לדוגמה: yo polymer:element carousel.
  • polymer:app משמש כדי לגבש את הקובץ index.html הראשוני, קובץ ה-Gruntfile.js שמכיל את תצורת זמן ה-build של הפרויקט וגם משימות של Graunt ומבנה תיקיות המומלץ לפרויקט. נוסף לכך, תהיה לכם אפשרות להשתמש ב-Sass Bootstrap בסגנונות של הפרויקט.

בואו נבנה אפליקציית פולימר

נבנה בלוג פשוט מאלמנטים של פולימר בהתאמה אישית ומהמחולל החדש שלנו.

אפליקציית Polymer

כדי להתחיל, עוברים לטרמינל, יוצרים ספרייה חדשה וממלאים בה cd באמצעות mkdir my-new-project && cd $_. עכשיו אפשר להתחיל את אפליקציית Polymer שלך על ידי ריצה:

$ yo polymer
בניית אפליקציות פולימרים

כך תשיגו את הגרסה העדכנית ביותר של Polymer מ-Buwer, ותיצרו אלמנטים של index.html, מבנה ספריות ומשימות גראנט עבור זרימת העבודה שלכם. למה לא לקחת קפה בזמן שאנחנו מחכים שהאפליקציה תסיים להתכונן?

אוקיי, עכשיו נוכל להריץ את grunt server כדי לראות תצוגה מקדימה של האפליקציה:

שרת גראנט

השרת תומך ב-LiveReload, כלומר אפשר להפעיל עורך טקסט ולערוך רכיב בהתאמה אישית. לאחר השמירה, הדפדפן ייטען מחדש. כך אפשר לקבל תצוגה יפה של המצב הנוכחי של האפליקציה בזמן אמת.

בשלב הבא ניצור רכיב פולימר חדש שייצג פוסט בבלוג.

$ yo polymer:element post
יצירת רכיב של פוסט

יאומן שואל אותנו כמה שאלות, למשל אם אנחנו רוצים לכלול בנאי או להשתמש ב-HTML Import כדי לכלול את רכיב הפוסט ב-index.html. נניח בינתיים שתי האפשרויות הראשונות 'לא' ונשאיר את האפשרות השלישית ריקה.

$ yo polymer:element post

[?] Would you like to include constructor=''? No

[?] Import to your index.html using HTML imports? No

[?] Import other elements into this one? (e.g 'another_element.html' or leave blank)

    create app/elements/post.html

ייווצר אלמנט פולימר חדש בספרייה /elements בשם post.html:

<polymer-element name="post-element"  attributes="">

    <template>

    <style>
        @host { :scope {display: block;} }
    </style>

    <span>I'm <b>post-element</b>. This is my Shadow DOM.</span>

    </template>

    <script>

    Polymer('post-element', {

        //applyAuthorStyles: true,

        //resetStyleInheritance: true,

        created: function() { },

        enteredView: function() { },

        leftView: function() { },

        attributeChanged: function(attrName, oldVal, newVal) { }

    });

    </script>

</polymer-element>

התוצאה מכילה:

עבודה עם מקור נתונים אמיתי

בבלוג שלנו צריך להיות מקום שבו אפשר יהיה לכתוב ולקרוא פוסטים חדשים. כדי להדגים עבודה עם שירות נתונים אמיתי, נשתמש בממשק ה-API של Google Apps Sheets. כך אנחנו יכולים לקרוא בקלות את התוכן של כל גיליון אלקטרוני שנוצר באמצעות Google Docs.

קדימה, מתחילים:

  1. בדפדפן (כדי לבצע את השלבים האלה, מומלץ להשתמש ב-Chrome) פותחים את הגיליון האלקטרוני הזה של Google Docs. הוא מכיל נתוני פוסטים לדוגמה בשדות הבאים:

    • מזהה
    • שם הסרטון
    • Author
    • Content
    • תאריך
    • מילות מפתח
    • כתובת אימייל (של המחבר)
    • חילזון (לכתובת ה-URL של החילזון של הפוסט)
  2. בתפריט קובץ, בוחרים באפשרות יצירת עותק כדי ליצור עותק משלכם של הגיליון האלקטרוני. אתם יכולים לערוך את התוכן בזמנכם החופשי, להוסיף או להסיר פוסטים.

  3. עבור לתפריט קובץ שוב ובחר פרסם באינטרנט.

  4. לוחצים על התחלת פרסום

  5. בקטע קבלת קישור לנתונים שפורסמו, בתיבת הטקסט האחרונה, מעתיקים את החלק של המפתח מכתובת ה-URL שסופקה. זה נראה כך: https://docs.google.com/spreadsheet/ccc?key=0AhcraNy3sgspdDhuQ2pvN21JVW9NeVA0M1h4eGo3RGc#gid=0

  6. מדביקים את ה-key בכתובת ה-URL הבאה, במקום שבו כתוב your-key-goes-here: https://spreadsheets.google.com/feeds/list/your-key-goes-here/od6/public/values?alt=json-in-script&callback=. דוגמה לשימוש במפתח שלמעלה יכולה להיראות כך: https://spreadsheets.google.com/feeds/list/0AhcraNy3sgspdDhuQ2pvN21JVW9NeVA0M1h4eGo3RGc/od6/public/values?alt=json-in-script

  7. אפשר להדביק את כתובת ה-URL בדפדפן ולעבור אליה כדי להציג את גרסת ה-JSON של התוכן בבלוג. רשום את כתובת האתר ולאחר מכן הקדישו זמן לבדיקת הפורמט של הנתונים האלה, כי תצטרכו לחזור עליה כדי להציג אותם במסך מאוחר יותר.

פלט ה-JSON בדפדפן עשוי להיראות קצת מרתיע, אבל אל דאגה! אנחנו באמת מעוניינים רק בנתונים של הפוסטים שלך.

ה-Google Sheets API מפיק פלט של כל אחד מהשדות בגיליון האלקטרוני של הבלוג עם קידומת מיוחדת post.gsx$. לדוגמה: post.gsx$title.$t, post.gsx$author.$t, post.gsx$content.$t וכן הלאה. כשנבצע איטרציה על כל "שורה" בפלט ה-JSON שלנו, נתייחס לשדות האלה כדי להחזיר את הערכים הרלוונטיים לכל פוסט.

עכשיו תוכלו לערוך את רכיב הפוסט המסולף החדש כדי bind חלקים של תגי העיצוב לנתונים בגיליון האלקטרוני. לשם כך, הוספנו את המאפיין post, שיקרא את כותרת הפוסט, המחבר, התוכן ושדות אחרים שיצרנו קודם. המאפיין selected (שאכלס אותו מאוחר יותר) משמש להצגת פוסט רק אם משתמש מנווט אל החילזון הנכון עבורו.

<polymer-element name="post-element" attributes="post selected">

    <template>

    <style>
        @host { :scope {display: block;} }
    </style>

        <div class="col-lg-4">

            <template if="[[post.gsx$slug.$t === selected]]">

            <h2>
                <a href="#[[post.gsx$slug.$t]]">
                [[post.gsx$title.$t  ]]
                </a>
            </h2>

            <p>By [[post.gsx$author.$t]]</p>

            <p>[[post.gsx$content.$t]]</p>

            <p>Published on: [[post.gsx$date.$t]]</p>

            <small>Keywords: [[post.gsx$keywords.$t]]</small>

            </template>

        </div>

    </template>

    <script>

    Polymer('post-element', {

        created: function() { },

        enteredView: function() { },

        leftView: function() { },

        attributeChanged: function(attrName, oldVal, newVal) { }

    });

    </script>

</polymer-element>

בשלב הבא ניצור רכיב בבלוג שמכיל גם אוסף של פוסטים וגם את הפריסה של הבלוג על ידי הפעלת yo polymer:element blog.

$ yo polymer:element blog

[?] Would you like to include constructor=''? No

[?] Import to your index.html using HTML imports? Yes

[?] Import other elements into this one? (e.g 'another_element.html' or leave blank) post.html

    create app/elements/blog.html

הפעם אנחנו מייבאים את הבלוג אל index.html באמצעות ייבוא HTML, כפי שאנחנו רוצים שהוא יופיע בדף. בהנחיה השלישית, מציינים באופן ספציפי את post.html כרכיב שרוצים לכלול.

כמו קודם, נוצר קובץ רכיב חדש (blog.html) ונוסף ל-/elements, הפעם מייבא את post.html וכולל <post-element> בתוך תג התבנית:

<link rel="import" href="post.html">

<polymer-element name="blog-element"  attributes="">

    <template>

    <style>
        @host { :scope {display: block;} }
    </style>

    <span>I'm <b>blog-element</b>. This is my Shadow DOM.</span>

        <post-element></post-element>

    </template>

    <script>

    Polymer('blog-element', {

        //applyAuthorStyles: true,

        //resetStyleInheritance: true,

        created: function() { },

        enteredView: function() { },

        leftView: function() { },

        attributeChanged: function(attrName, oldVal, newVal) { }

    });

    </script>

</polymer-element>

מכיוון שביקשנו לייבא את רכיב הבלוג באמצעות ייבוא HTML (דרך להכללה ולשימוש חוזר במסמכי HTML במסמכי HTML אחרים) לאינדקס שלנו, אנו גם יכולים לוודא שהוא נוסף כראוי למסמך <head>:

<!doctype html>
    <head>

        <meta charset="utf-8">

        <meta http-equiv="X-UA-Compatible" content="IE=edge">

        <title></title>

        <meta name="description" content="">

        <meta name="viewport" content="width=device-width">

        <link rel="stylesheet" href="styles/main.css">

        <!-- build:js scripts/vendor/modernizr.js -->

        <script src="bower_components/modernizr/modernizr.js"></script>

        <!-- endbuild -->

        <!-- Place your HTML imports here -->

        <link rel="import" href="elements/blog.html">

    </head>

    <body>

        <div class="container">

            <div class="hero-unit" style="width:90%">

                <blog-element></blog-element>

            </div>

        </div>

        <script>
        document.addEventListener('WebComponentsReady', function() {
            // Perform some behaviour
        });
        </script>

        <!-- build:js scripts/vendor.js -->

        <script src="bower_components/polymer/polymer.min.js"></script>

        <!-- endbuild -->

</body>

</html>

מצוין.

הוספת יחסי תלות באמצעות Bower

עכשיו נערוך את הרכיב שלנו כך שיעשה שימוש ברכיב Polymer JSONP לקריאה ב-publish.json. כדי לקבל את המתאם אפשר לשכפל git או להתקין את polymer-elements דרך Bower על ידי הרצת bower install polymer-elements.

יחסי תלות של Bower

אחרי שמתקינים את כלי השירות, צריך לכלול אותו בתור ייבוא ברכיב blog.html עם:

<link rel="import" href="../bower_components/polymer-jsonp/polymer-jsonp.html">

בשלב הבא, כוללים את התג הזה ומצרפים את ה-url לגיליון האלקטרוני של הפוסטים הקודמים בבלוג שלנו, ומוסיפים את &callback= בסוף:

<polymer-jsonp auto url="https://spreadsheets.google.com/feeds/list/your-key-value/od6/public/values?alt=json-in-script&callback=" response="[[posts]]"></polymer-jsonp>

עכשיו אנחנו יכולים להוסיף תבניות כדי לערוך איטרציה בגיליון האלקטרוני אחרי קריאתו. הפלט הראשון יוצר תוכן עניינים, עם קישור לכותרת של פוסט שמצביע על החילזון הזה.

<!-- Table of contents -->

<ul>

    <template repeat="[[post in posts.feed.entry]]">

    <li><a href="#[[post.gsx$slug.$t]]">[[post.gsx$title.$t]]</a></li>

    </template>

</ul>

השני מעבד מופע אחד של post-element לכל רשומה שנמצאה, ומעביר את תוכן הפוסט אליו בהתאם. שימו לב שאנחנו מעבירים דרך המאפיין post שמייצג את תוכן הפוסט בשורה אחת בגיליון אלקטרוני, ודרך מאפיין selected שנאכלס אותו במסלול.

<!-- Post content -->

<template repeat="[[post in posts.feed.entry]]">

    <post-element post="[[post]]" selected="[[route]]"></post-element>

</template>

המאפיין repeat שמופיע בתבנית שלנו יוצר ושומר מכונה עם [[ links ]] לכל רכיב באוסף המערכים של הפוסטים שלנו, כשהוא מופיע.

אפליקציית Polymer

עכשיו, כדי שנוכל לאכלס את ה-[[route]] הנוכחי, נתחיל לרמות ולהשתמש בספרייה שנקראת Flatiron Director (מנהל Flatiron) שמקושרת ל-[[route]] בכל פעם שהגיבוב של כתובת ה-URL משתנה.

למרבה המזל, יש אלמנט פולימר (חלק מחבילת הרכיבים הנוספים) שאפשר להשיג אותו. אחרי ההעתקה לספרייה /Elements, אנחנו יכולים להפנות אליה עם <flatiron-director route="[[route]]" autoHash></flatiron-director>, ולציין את route בתור הנכס שאליו אנחנו רוצים לקשר את החשבון, ולבקש ממנו לקרוא באופן אוטומטי את הערך של שינויי גיבוב (autoHash).

עכשיו אנחנו מקבלים את הדברים הבאים:

    <link rel="import" href="post.html">

    <link rel="import" href="polymer-jsonp/polymer-jsonp.html">

    <link rel="import" href="flatiron-director/flatiron-director.html">

    <polymer-element name="blog-element"  attributes="">

      <template>

        <style>
          @host { :scope {display: block;} }
        </style>

        <div class="row">

          <h1><a href="/#">My Polymer Blog</a></h1>

          <flatiron-director route="[[route]]" autoHash></flatiron-director>

          <h2>Posts</h2>

          <!-- Table of contents -->

          <ul>

            <template repeat="[[post in posts.feed.entry]]">

              <li><a href="#[[post.gsx$slug.$t]]">[[post.gsx$title.$t]]</a></li>

            </template>

          </ul>

          <!-- Post content -->

          <template repeat="[[post in posts.feed.entry]]">

            <post-element post="[[post]]" selected="[[route]]"></post-element>

          </template>

        </div>

        <polymer-jsonp auto url="https://spreadsheets.google.com/feeds/list/0AhcraNy3sgspdHVQUGd2M2Q0MEZnRms3c3dDQWQ3V1E/od6/public/values?alt=json-in-script&callback=" response="[[posts]]"></polymer-jsonp>

      </template>

      <script>

        Polymer('blog-element', {

          created: function() {},

          enteredView: function() { },

          leftView: function() { },

          attributeChanged: function(attrName, oldVal, newVal) { }

        });

      </script>

    </polymer-element>
אפליקציית Polymer

יש! עכשיו יש לנו בלוג פשוט שקורא נתונים מ-JSON ומשתמש בשני אלמנטים של פולימר שדורגו ב-Yeoman.

עבודה עם אלמנטים של צד שלישי

המערכת האקולוגית של הרכיבים סביב רכיבי האינטרנט גדלה לאחרונה, ומופיעים אתרים של גלריית רכיבים כמו customelements.io. לאחר שבדקתי את הרכיבים שנוצרו על ידי הקהילה, מצאתי פרופיל לשליפת פרופילי נגרות ואנחנו יכולים לשלוף אותו ולהוסיף אותו גם לאתר הבלוג שלנו.

דף הבית של רכיבים מותאמים אישית

מעתיקים את מקורות אלמנט הגרירה לספרייה /elements, כוללים אותם באמצעות ייבוא HTML ב-post.html, ולאחר מכן מוסיפים את לתבנית, מעבירים את שדה האימייל מהגיליון האלקטרוני שלנו כמקור שם המשתמש. בום!

<link rel="import" href="gravatar-element/src/gravatar.html">

<polymer-element name="post-element" attributes="post selected">

    <template>

    <style>
        @host { :scope {display: block;} }
    </style>

        <div class="col-lg-4">

            <template if="[[post.gsx$slug.$t === selected]]">

            <h2><a href="#[[post.gsx$slug.$t]]">[[post.gsx$title.$t]]</a></h2>

            <p>By [[post.gsx$author.$t]]</p>

            <gravatar-element username="[[post.gsx$email.$t]]" size="100"></gravatar-element>

            <p>[[post.gsx$content.$t]]</p>

            <p>[[post.gsx$date.$t]]</p>

            <small>Keywords: [[post.gsx$keywords.$t]]</small>

            </template>

        </div>

    </template>

    <script>

    Polymer('post-element', {

        created: function() { },

        enteredView: function() { },

        leftView: function() { },

        attributeChanged: function(attrName, oldVal, newVal) { }

    });

    </script>

</polymer-element>

בואו נראה מה זה נותן לנו:

אפליקציית פולימר עם רכיבים מותאמים אישית

יפה!

בתוך זמן קצר יחסית, יצרנו אפליקציה פשוטה שמורכבת מכמה רכיבי אינטרנט, בלי שנצטרך לדאוג לכתוב קוד סטנדרטי (boilerplate), להוריד יחסי תלות באופן ידני או להגדיר שרת מקומי או תהליך עבודה של build.

אופטימיזציה של האפליקציה

תהליך העבודה של Yaoman כולל פרויקט קוד פתוח נוסף בשם Grunt – מפעיל משימות שיכול להריץ מספר משימות ספציפיות ל-build (מוגדרות ב-Gruntfile) כדי ליצור גרסה אופטימלית של האפליקציה. הרצת grunt בפני עצמה תריץ משימה של default שהמחולל הגדיר לצורך איתור שגיאות בקוד, בדיקה ובנייה:

grunt.registerTask('default', [

    'jshint',

    'test',

    'build'

]);

משימת jshint שלמעלה תיבדק באמצעות קובץ .jshintrc שלכם כדי ללמוד את ההעדפות שלכם, ולאחר מכן תריצו אותה מול כל קובצי ה-JavaScript בפרויקט שלכם. לפירוט המלא של האפשרויות ב-JSHint, אפשר לעיין במסמכים.

המשימה של test נראית בערך כך, והיא יכולה ליצור ולהציג את האפליקציה שלך במסגרת הבדיקה שאנחנו ממליצים עליה, Mocha. הוא גם יבצע עבורך את הבדיקות:

grunt.registerTask('test', [

    'clean:server',

    'createDefaultTemplate',

    'jst',

    'compass',

    'connect:test',

    'mocha'

]);

מאחר שהאפליקציה שלנו במקרה הזה פשוטה למדי, נרשום את כל הבחינה במסגרת פעילות נפרדת. יש עוד כמה דברים שחשוב לנו לטפל בהם בתהליך ה-build, אז נראה מה עושה משימת grunt build שהוגדרה ב-Gruntfile.js:

grunt.registerTask('build', [

    'clean:dist',    // Clears out your .tmp/ and dist/ folders

    'compass:dist',  // Compiles your Sassiness

    'useminPrepare', // Looks for <!-- special blocks --> in your HTML

    'imagemin',      // Optimizes your images!

    'htmlmin',       // Minifies your HTML files

    'concat',        // Task used to concatenate your JS and CSS

    'cssmin',        // Minifies your CSS files

    'uglify',        // Task used to minify your JS

    'copy',          // Copies files from .tmp/ and app/ into dist/

    'usemin'         // Updates the references in your HTML with the new files

]);

מריצים את גרסת grunt build וגרסת האפליקציה מוכנה לסביבת הייצור צריכה להיות מוכנה למשלוח. בואו ננסה את זה.

גרסת build של GRunt

זהו!

אם נתקעת, אפשר למצוא גרסה מוכנה מראש של polymer-blog בכתובת https://github.com/addyosmani/polymer-blog.

מה עוד יש לנו?

רכיבי האינטרנט עדיין נמצאים במצב אבולוציה, וכך גם הכלים שמסביבם.

אנחנו בודקים כרגע איך אפשר לשרשר את ייבוא ה-HTML שלהם כדי לשפר את ביצועי הטעינה באמצעות פרויקטים כמו Vulcanize (כלי של פרויקט Polymer), ואיך המערכת העסקית של הרכיבים תוכל לעבוד עם מנהל חבילות כמו Bower.

נודיע לך כשיהיו לנו תשובות טובות יותר לשאלות האלה, אבל לפני כן נעדכן אותך על זמנים מלהיבים.

התקנה עצמאית של פולימר עם Bower

אם אתם מעדיפים התחלה קלה יותר ל-Polymer, אפשר להתקין אותה בנפרד ישירות מ-Bower על ידי הרצת:

bower install polymer

שיוסיף אותו לספריית boer_components. לאחר מכן תוכלו להפנות אליו באופן ידני באינדקס של האפליקציה ולעצב את העתיד.

מה דעתך?

עכשיו אתם יודעים איך לגבש אפליקציית פולימר באמצעות Web Components (רכיבי אינטרנט) עם Yoman. אם יש לכם משוב על המחולל, נשמח לשמוע על כך בתגובות. לחלופין, תוכלו לדווח על באג או לפרסם הודעה בכלי למעקב אחר בעיות ב-Yeoman. נשמח לדעת אם יש משהו נוסף שהיית רוצה לראות שהמחולל עושה טוב יותר, כי נוכל לשפר רק את השימוש שלך והמשוב שלך :)