Wie Sie mit Magic Scaffolding Dateien automatisch erstellen und Zeit sparen

Bevor wir beginnen: Dieser Artikel verwendet JavaScript/Node.js-Beispielcode, aber Sie können diese Konzepte mit den richtigen Tools in jede Sprache portieren.

Ein spannendes Intro

Fühlen Sie sich manchmal dabei, in Ihren Projekten immer wieder dieselben Dateien zu erstellen?

Ich auch.

Meine Finger tun weh!

Ich bin nicht überrascht. Sie nehmen den Robotern Arbeit ab.

Das wiederholte Erstellen derselben Dateien ist langweilig und unnötig.

TLDR? Ich habe dich – Hier ist eine Demo

Zeig mir den Code

Ich respektiere Ihren Sinn für Dringlichkeit – ich komme zur Sache.

Der Kodex

Wir wollen die Dateierstellung automatisieren – deshalb seid ihr heute alle gekommen. Zuerst müssen wir die Dateien identifizieren, die wir erstellen möchten.

Ich habe in letzter Zeit viele React-Komponenten erstellt, also dreht sich mein Setup darum – aber Sie können dies für buchstäblich alles optimieren.

Ich habe dies in vier Schritte aufgeteilt. Ich sage es Ihnen jetzt nur, damit Sie Ihre Erwartungen verwalten können. Wenn Sie nicht länger als drei Schritte bewältigen können, haben wir ein Problem...

Schritt 1:Vorlagen

Einmal einrichten und profitieren.

Wir brauchen Vorlagen. Ich habe Vorlagenliterale verwendet, aber tun Sie es so, wie es für Sie sinnvoll ist – seien Sie kreativ.

Dies sind die Dateien, die ich jedes Mal erstelle, wenn ich eine React-Komponente erstelle:

  1. index.jsx
  2. {Komponente}.test.js
  3. {Komponente}.sass

Hinweis: {Component} impliziert String-Interpolation.

Ich teste mit Jest und verwende die Boilerplate „Create-React-App“. Ich weiß, dass viele Leute heutzutage CSS in JS bevorzugen – aber hey. Lass mich in den Kommentaren wissen, was dir gefällt.

Wie auch immer – los geht's:

const templates = {
  
  index: name => `// @flow
import React from 'react';
import './${name}.css';
// TODO: write rest of ${name} component
const ${name} = () => (
  <div className="${name.toLowerCase()}">
    <span>rest of component</span>
  </div>
);
export default ${name};`,
  
  test: name => `// TODO: TDD
import { shallow, render } from 'enzyme';
import renderer from 'react-test-renderer';
import React from 'react';
import ${name} from '.';
const component = <${name} />;
describe('The ${name} component', () => {
  it('renders correctly', () => {
    const wrapper = render(component);
    expect(wrapper.hasClass('${name.toLowerCase()}')).toBeTruthy();
    const tree = renderer.create(component).toJSON();
    expect(tree).toMatchSnapshot();
  });
});`,
  
  sass: name => `.${name.toLowerCase()}
  background: initial`,
};

Das ist der chaotischste Code, den Sie hier sehen werden – Pinky Promise.

Wir haben also ein Objekt mit drei Eigenschaften:index, test und sass. Jeder hostet eine Funktion, die einen Namen annimmt und eine Vorlage mit diesem interpolierten Namen zurückgibt. Scheint legitim.

Schritt 2:Lassen Sie uns einige Funktionen erstellen!

Wir verwenden das mit Node. Es ist fabelhaft. Es macht viele Dinge.

Wir werden einige Pfeilfunktionen und ein wenig funktionale Programmierung verwenden. Haben Sie keine Angst – machen Sie einfach mit.

Die Syntax der Doppelpfeilfunktion wird Currying genannt. Es ist in Ordnung, wenn es komisch aussieht. Ich war ausgeflippt, als ich es zum ersten Mal sah, aber es ermöglicht super coole Sachen. Hier ist tatsächlich eine kurze Demo:

const fs = require('fs');

const fileExists = path => file => fs.existsSync(`${path}/${file}`);

const fileExistsInSrc = fileExists('/src'); // file => fs.existsSync(`${path}/${file}`)

fileExistsInSrc('index.js') // true || false

Das ist also Curry mit teilweiser Anwendung – es ist auch ein Abschluss.

Seitenleiste :Hoffentlich ruft mich hier niemand aus technischen Gründen an, aber bitte belästigen Sie mich in den Kommentaren, wenn Sie das Bedürfnis haben.

Machen wir weiter:

const fs = require('fs');

const fileExists = path => file => fs.existsSync(`${path}/${file}`);

const writeToPath = path => (file, content) => {
  const filePath = `${path}/${file}`;

  fs.writeFile(filePath, content, err => {
    if (err) throw err;
    console.log("Created file: ", filePath);
    return true;
  });
};

Zuerst benötigen wir fs . Wir brauchen es in unserem Leben.

Dann deklarieren wir fileExists als Funktionsausdruck.

Schließlich haben wir noch einen weiteren Funktionsausdruck namens writeToPath. Es nimmt den Pfad und gibt eine andere Funktion zurück, die eine Datei akzeptiert Zeichenfolge und den Inhalt dieser Datei. Es schreibt dann die Datei oder wirft einen Fehler (im schlimmsten Fall).

Verstehst du es richtig? Wir erstellen einige Dateien.

Schritt 3:Treffen Sie Chokidar

Fun Fact:Es ist ein Hindi-Wort.

Chowkidar — (Indien ) Wächter, Hausmeister, Pförtner; jemand, der ein „Chowki“, eine Polizeistation oder ein Wachhaus bewohnt.

Wir sprechen jedoch über das npm-Paket. Es basiert auf unserem neuen Freund fs und Sie können es für so viele schöne Dinge verwenden.

Es überwacht unsere Dateien für uns wie ein Falke.

Nun, nicht gerade wie ein Falke.

Es ist kein Vogel.

Wie überhaupt.

Wie auch immer, hier ist der Code …

const chokidar = require("chokidar");

const watcher = chokidar
  .watch("src/components/**", { ignored: /node_modules/ })
  .on("addDir", (path, event) => {
    const name = path.replace(/.*\/components\//, "");
    const goodToGo = /^[^\/_]*$/.test(name);
    if (goodToGo) createFiles(path, name);
  });

Zuerst benötigen wir es.

Als nächstes definieren wir, was wir sehen wollen. Ich beobachte die src/components Verzeichnis, aber Sie können jeden Satz von Pfaden beobachten. Sie können sogar eine Reihe von Wegen passieren. Wenn Sie ** nicht erkennen part in src/components/** – wird als Glob-Muster bezeichnet.

Danach definieren wir, auf welche Ereignisse wir hören wollen. Ich warte nur auf das Hinzufügen eines Verzeichnisses mit .on("addDir") Sie können aber auch nach anderen Ereignissen lauschen.

Als nächstes extrahieren wir den Namen der Komponente, indem wir alles vor dem Komponentennamen ersetzen:

src/components/Header/components/Title

wird

Title

Schließlich prüfen wir, ob der Komponentenname diese Regex passiert:

/^[^\/_]*$/

Solange es also keinen Schrägstrich oder Unterstrich hat, ist es gut zu gehen. Dadurch wird verhindert, dass __tests__-Ordner oder verschachtelte/Verzeichnisse versehentlich verschmutzt werden.

Schritt 4:Zeit, einige Dateien zu erstellen!

Sie haben den letzten Schritt erreicht. Herzliche Glückwünsche! Es war ziemlich großartig.

Diese nächste Funktion heißt treffend createFiles .

Es ist ein bisschen chaotisch – es könnte umgestaltet werden.

Ich entschuldige mich im Voraus, wenn Sie der unten stehende Code beleidigt.

Lassen Sie uns ins Detail gehen:

function createFiles(path, name) {
  const files = {
    index: "index.jsx",
    test: `${name}.test.js`,
    sass: `${name}.sass`
  };

  if (name !== "components") {
    const writeFile = writeToPath(path);
    const toFileMissingBool = file => !fileExists(path)(file);
    const checkAllMissing = (acc, cur) => acc && cur;

    const noneExist = Object.values(files)
      .map(toFileMissingBool)
      .reduce(checkAllMissing);

    if (noneExist) {
      console.log(`Detected new component: ${name}, ${path}`);
      Object.entries(files).forEach(([type, fileName]) => {
        writeFile(fileName, templates[type](name));
      });
    }
  }
}

Ganz oben deklarieren wir also die Dateien object – es ist eine Liste von Dateinamen-Strings, denen wir den Namen hinzufügen Parameter in. Sie haben vielleicht bemerkt, dass es dieselben Schlüssel wie die Vorlagen hat Objekt. Das ist wichtig.

Das wenn Aussage ist sehr spezifisch für mein Setup. Ich möchte meine Dateien nicht wenn erstellen Der neue Ordner heißt Komponenten. Ich erstelle Komponenten nur innerhalb einem Komponenten-Unterordner.

  • writeFile ist unsere Funktion writeToPath teilweise angewendet. Es ist eine Funktion, die eine Datei im angegebenen Pfad erstellt, wenn sie mit einem Dateinamen und etwas Inhalt aufgerufen wird.
  • toFileMissingBool nimmt einen Dateinamen und gibt true zurück, wenn diese Datei im angegebenen Pfad nicht existiert. Ich weiß, dass die Funktionsnamen seltsam sind, aber ich verspreche, dass es in ein paar Zeilen mehr Sinn ergibt.
  • checkAllMissing ist eine Funktion, die wir zum Reduzieren übergeben werden . Es nimmt zwei boolesche Werte und gibt wahr zurück, wenn beide wahr sind. Das ist Boolesche Algebra. Wir verwenden auch das Reduzieren Methode von Array . Keine Angst vor dem Reduzieren. Es ist super cool und in solchen Situationen wirklich nützlich.

Lassen Sie uns über die Variable noneExist sprechen . Wenn dies zutrifft, ist keine der Dateien, die wir erstellen möchten, im neuen Ordner vorhanden. Die Idee ist, dass Sie sich nicht mit einem Ordner anlegen, nur weil er keine Testdatei oder Sass-Datei enthält. Vielleicht braucht dieser Ordner keinen.

const noneExist = Object.values(files)
  .map(toFileMissingBool)      
  .reduce(checkAllMissing);

Aus diesem Grund habe ich diese seltsam benannten Funktionen oben erstellt.

Wir kartieren die Werte in Dateien zu einem Boolean was darstellt, ob diese Datei fehlt oder nicht. Dann nehmen wir dieses Array von booleschen Werten und reduzieren sie zu einem einzigen Boolean Wert, der angibt, ob alle Dateien vorhanden sind oder nicht.

Wenn sie also alle wahr sind dann noneExist ist auch wahr. Aber wenn auch nur einer falsch ist dann noneExist wird false sein .

Ich hoffe, du hast das alles. Es ist ein bisschen wie ein Schluck.

Letztes Stück Code:

Object.entries(files).forEach(([type, fileName]) => {
  writeFile(fileName, templates[type](name)); 
});

Wir nehmen den Schlüssel (Typ) und Wert (fileName) und schreibe eine Datei in den angegebenen Pfad mit dem Inhalt aus der entsprechenden Vorlage.

Fin.

Dieses Bild einer Meeresschildkröte zeigt, wie frei Sie sich fühlen müssen, nachdem Sie alles automatisiert haben.

Wenn Sie den gesamten Code zum automatischen Erstellen von Reaktionskomponenten möchten, finden Sie ihn hier.

Lassen Sie mich wissen, was Sie dachten – bleiben Sie in Kontakt.

Teilen Sie mir mit, wenn Sie Fehler finden.

Folgen Sie mir auf Twitter, Medium oder Github.