MAM: Frontend-Montage ohne Schmerzen

Hallo, mein Name ist Dmitry Karlovsky und ich ... verehre MAM. M AM regiert die gnostischen M- Module und erspart mir den Löwenanteil der Routine.


Typisches Agnostikmodul


Ein Agnostikmodul ist im Gegensatz zum herkömmlichen Modul keine Datei mit einer Quelle, sondern ein Verzeichnis, in dem sich Quellen in verschiedenen Sprachen befinden können: Programmlogik in JS / TS , Tests in TS / JS , Zusammensetzung der Komponenten in view.tree , Stile in CSS Lokalisierung im locale=*.json , Bilder usw. usw. Falls gewünscht, ist es nicht schwierig, die Unterstützung einer anderen Sprache zu befestigen. Zum Beispiel Stylus zum Schreiben von Stilen oder HTML zum Beschreiben von Vorlagen.


Abhängigkeiten zwischen Modulen werden automatisch durch Analyse der Quelle verfolgt. Wenn das Modul eingeschaltet ist, wird es als Ganzes eingeschaltet - jeder Modulquellcode wird transponiert und fällt in das entsprechende Paket: Skripte - separat, Stile - separat, Tests - separat. Für verschiedene Plattformen - ihre Bundles: für einen Knoten - ihre eigenen, für einen Browser - ihre eigenen.


Volle Automatisierung, fehlende Konfiguration und Boilerplate, minimale Bündelgrößen, automatisches Pumpen von Abhängigkeiten, Entwicklung von Hunderten von entfremdeten Bibliotheken und Anwendungen in einer Codebasis ohne Schmerzen und Leiden. Wow was für eine Sucht! Entfernen Sie schwangere, nervöse Kinder von den Monitoren und begrüßen Sie das U-Boot!


Philosophie


MAM ist ein mutiges Experiment, um die Art und Weise, wie Code organisiert ist, und den Prozess der Arbeit damit radikal zu ändern. Hier sind die Grundprinzipien:


Konventionen statt Konfiguration. Angemessene, einfache und universelle Vereinbarungen ermöglichen es Ihnen, die gesamte Routine zu automatisieren und gleichzeitig die Bequemlichkeit und Einheitlichkeit zwischen verschiedenen Projekten zu gewährleisten.


Die Infrastruktur ist getrennt, der Code ist getrennt. Eine Situation ist nicht ungewöhnlich, wenn Sie Dutzende oder sogar Hunderte von Bibliotheken und Anwendungen entwickeln müssen. Stellen Sie nicht für jeden die Infrastruktur für Montage, Entwicklung, Bereitstellung usw. bereit. Es reicht aus, es einmal zu fragen und dann Anwendungen wie Torten zu nieten.


Zahlen Sie nicht für das, was Sie nicht verwenden. Sie verwenden eine Art Modul - es ist im Bundle mit all seinen Abhängigkeiten enthalten. Nicht verwenden - lässt sich nicht einschalten. Je kleiner die Module sind, desto größer ist die Granularität und desto weniger unnötiger Code im Bundle.


Minimaler redundanter Code. Das Aufteilen von Code in Module sollte so einfach sein wie das Schreiben des gesamten Codes in eine einzige Datei. Andernfalls ist der Entwickler faul, große Module in kleine zu zerlegen.


Keine Versionskonflikte. Es gibt nur eine Version - die aktuelle. Es ist nicht erforderlich, Ressourcen für die Unterstützung alter Versionen aufzuwenden, wenn Sie diese für die Aktualisierung letzterer verwenden können.


Halten Sie einen Finger am Puls der Zeit. Das schnellste Feedback zu Inkompatibilitäten lässt den Code nicht schlecht werden.


Der einfachste Weg ist der sicherste. Wenn der richtige Weg zusätzliche Anstrengungen erfordert, stellen Sie sicher, dass niemand zu ihnen geht.


Importe / Exporte


Wir eröffnen das erste Projekt, das mit einem modernen Modulsystem entstanden ist: Ein Modul ist weniger als 300 Zeilen lang, 30 davon sind Importe.


Aber das sind immer noch Blumen: Für eine Funktion von 9 Zeilen sind 8 Importe erforderlich.


Und mein Favorit: Keine einzige Zeile nützlichen Codes. 20 Zeilen mit Verschiebungswerten aus dem Modulhaufen in eine, sodass Sie später von einem Modul und nicht von zwanzig importieren können.


All dies ist ein Boilerplate, was dazu führt, dass Entwickler zu faul sind, um kleine Codeteile in separate Module zu unterteilen, und große Module kleinen vorziehen. Und selbst wenn sie nicht faul sind, ergibt sich entweder viel Code zum Importieren kleiner Module oder spezielle Module, die viele Module in sich selbst importieren und alle in großen Mengen exportieren.


All dies führt zu einer geringen Granularität des Codes und zum Aufblasen der Größe von Bundles mit nicht verwendetem Code, was das Glück hat, dem verwendeten Code nahe zu kommen. Für JS versuchen sie, dieses Problem zu lösen, indem sie die Assembly-Pipeline komplizieren und das sogenannte "Tree-Shaking" hinzufügen, das den Überschuss von dem, was Sie importiert haben, abschneidet. Dies verlangsamt die Montage, aber nicht alles wird geschnitten.


Idee: Was ist, wenn wir nicht importieren, sondern nur nehmen und verwenden und der Sammler selbst herausfindet, was importiert werden muss?


Moderne IDEs können automatisch Importe für die von Ihnen verwendeten Entitäten generieren. Wenn die IDE dies kann, was hindert den Kollektor dann daran? Es reicht aus, eine einfache Vereinbarung über die Benennung und den Speicherort von Dateien zu treffen, die für den Benutzer bequem und für die Maschine verständlich wäre. PHP hat seit langem eine solche Standardkonvention: PSR-4 . MAM führt dasselbe für .ts- und .jam.js-Dateien ein: Namen, die mit $ beginnen, sind der vollständig qualifizierte Name einer globalen Entität, deren Code entlang des von FQN erhaltenen Pfads geladen wird, indem Trennzeichen durch Schrägstriche ersetzt werden. Ein einfaches Beispiel für zwei Module:


my / alert / alert.ts


 const $my_alert = alert // FQN    

meine / app / app.ts


 $my_alert( 'Hello!' ) // ,   /my/alert/ 

Ein ganzes Modul aus einer Zeile - was könnte einfacher sein? Das Ergebnis lässt nicht lange auf sich warten: Die einfache Erstellung und Verwendung von Modulen führt zu einer Minimierung ihrer Größe. Infolgedessen wird die Granularität maximiert. Und wie eine Kirsche - Minimierung der Bündelgröße ohne Baumschütteln.


Ein gutes Beispiel ist die JSON / mol / data- Familie von Validierungsmodulen. Wenn Sie die Funktion $mol_data_integer irgendwo in Ihrem Code verwenden, werden die Module /mol/data/integer und /mol/data/number , von denen $mol_data_integer abhängt, in das Bundle aufgenommen. Zum Beispiel liest der Collector /mol/data/email nicht einmal von der Festplatte, da niemand davon abhängt.


Ein Chaos harken


Seit wir Angular treten, werden wir nicht aufhören. Wo sollten Sie nach der applyStyles Funktionsdeklaration suchen? Sie werden /packages/core/src/render3/styling_next/bindings.ts nicht erraten. Die Möglichkeit, irgendwo etwas zu platzieren, führt dazu, dass wir in jedem Projekt ein eindeutiges Dateispeicherungssystem beobachten, das häufig keiner Logik zugänglich ist. Und wenn die IDE häufig durch den „Sprung zur Definition“ gespeichert wird, wird diese Möglichkeit dem Anzeigen des Codes auf dem Github oder dem Überprüfen der Pull-Anforderung entzogen.


Idee: Was ist, wenn Entitätsnamen genau ihrem Standort entsprechen?


Um den Code in der Datei /angular/packages/core/src/render3/stylingNext/bindings.ts , müssen Sie in der MAM-Architektur die Entität $angular_packages_core_src_render3_stylingNext_applyStyles , aber natürlich wird niemand handeln, da der Name so viel mehr enthält. Aber die Namen im Code, die ich sehen möchte, sind kurz und prägnant, sodass der Entwickler versuchen wird, alles Unnötige aus dem Namen auszuschließen, wobei nur das Wichtige $angular_render3_applyStyles : $angular_render3_applyStyles . Und es befindet sich entsprechend in /angular/render3/applyStyles/applyStyles.ts .


Beachten Sie, wie MAM die Schwächen von Entwicklern nutzt, um das gewünschte Ergebnis zu erzielen: Jede Entität erhält einen kurzen, global eindeutigen Namen, der in jedem Kontext verwendet werden kann. In den Commit-Nachrichten können Sie beispielsweise mit diesen Namen schnell und genau erfassen, worüber sie sprechen:


 73ebc45e517ffcc3dcce53f5b39b6d06fc95cae1 $mol_vector: range expanding support 3a843b2cb77be19688324eeb72bd090d350a6cc3 $mol_data: allowed transformations 24576f087133a18e0c9f31e0d61052265fd8a31a $mol_data_record: support recursion 

Oder nehmen wir an, Sie möchten alle Erwähnungen des Moduls $ mol_fiber im Internet finden - dank FQN ist dies einfacher als je zuvor.


Zyklische Abhängigkeiten


Schreiben wir 7 Zeilen einfachen Codes in eine Datei:


 export class Foo { get bar() { return new Bar(); } } export class Bar extends Foo {} console.log(new Foo().bar); 

Trotz der zyklischen Abhängigkeit funktioniert es korrekt. Wir teilen es in 3 Dateien auf:


my / foo.js.


 import { Bar } from './bar.js'; export class Foo { get bar() { return new Bar(); } } 

my / bar.js.


 import { Foo } from './foo.js'; export class Bar extends Foo {} 

meine / app.js.


 import { Foo } from './foo.js'; console.log(new Foo().bar); 

Ups, ReferenceError: Cannot access 'Foo' before initialization . Was für ein Unsinn? Um dies zu beheben, muss unsere app.js wissen, dass foo.js von foo.js abhängt. Daher müssen wir zuerst bar.js importieren, das foo.js importiert foo.js Danach können wir foo.js bereits fehlerfrei importieren:


meine / app.js.


 import './bar.js'; import { Foo } from './foo.js'; console.log(new Foo().bar); 

Diese Browser, dieses NodeJS, dieses Webpack, dieses Paket - sie alle arbeiten schief mit zirkulären Abhängigkeiten. Und nun, sie würden es ihnen einfach verbieten - man könnte den Code sofort komplizieren, so dass es keine Schleifen gibt. Aber sie können gut funktionieren und dann bam und einen unverständlichen Fehler geben.


Idee: Was ist, wenn wir während der Montage die Dateien nur in der richtigen Reihenfolge kleben, als ob der gesamte Code ursprünglich in einer Datei geschrieben wäre?


Teilen wir den Code nach den Prinzipien von MAM auf:


meine / foo / foo.ts


 class $my_foo { get bar() { return new $my_bar(); } } 

my / bar / bar.ts.


 class $my_bar extends $my_foo {} 

meine / app / app.ts


 console.log(new $my_foo().bar); 

Die gleichen 7 Codezeilen, die ursprünglich waren. Und sie arbeiten einfach ohne zusätzlichen Schamanismus. Die Sache ist, dass der Sammler versteht, dass die Abhängigkeit von my/bar von my/foo strenger ist als die von my/foo von my/bar . Dies bedeutet, dass Sie diese Module in dieser Reihenfolge in das Bundle aufnehmen sollten: my/foo , my/bar , my/app .


Wie versteht der Sammler das? Jetzt ist die Heuristik einfach - durch die Anzahl der Einrückungen in der Zeile, in der die Abhängigkeit erkannt wird. Bitte beachten Sie, dass eine stärkere Abhängigkeit in unserem Beispiel keine Einrückung und eine schwache eine doppelte Einrückung aufweist.


Verschiedene Sprachen


Es ist einfach so passiert, dass wir für verschiedene Dinge verschiedene Sprachen für diese verschiedenen Dinge geschärft haben. Am häufigsten sind: JS, TS, CSS, HTML, SVG, SCSS, Less, Stylus. Jedes hat ein eigenes Modulsystem, das in keiner Weise mit anderen Sprachen interagiert. Unnötig zu erwähnen, dass es ungefähr 100500 Arten spezifischerer Sprachen gibt. Um eine Komponente zu verbinden, müssen Sie daher ihre Skripte, Stile, Register separat registrieren, die Bereitstellung der benötigten statischen Dateien separat konfigurieren usw. usw. separat verbinden.


Dank Loadern versucht Webpack, dieses Problem zu lösen. Aber er hat einen Einstiegspunkt ist ein Skript, das bereits Dateien in anderen Sprachen verbindet. Und wenn wir kein Skript brauchen? Zum Beispiel haben wir ein Modul mit schönen Stilen für Teller und wir möchten, dass sie im hellen Thema die gleichen Farben haben und andere im dunklen:


 .dark-theme table { background: black; } .light-theme table { background: white; } 

Wenn wir vom Thema abhängig sind, sollte außerdem ein Skript geladen werden, das das gewünschte Thema je nach Tageszeit installiert. Das heißt, CSS hängt tatsächlich von JS ab.


Idee: Was ist, wenn ein modulares System nicht von Sprachen abhängt?


Da in MAM das modulare System von Sprachen getrennt ist, können die Abhängigkeiten sprachübergreifend sein. CSS kann von JS abhängen, was von TS abhängen kann, was von einem anderen JS abhängen kann. Dies wird durch die Tatsache erreicht, dass Quellabhängigkeiten von den Modulen erkannt werden und die Module vollständig verbunden sind und Quellcodes in beliebigen Sprachen enthalten können. Im Fall des Themenbeispiels sieht es so aus:


/my/table/table.css


 /* ,   /my/theme */ [my_theme="dark"] table { background: black; } [my_theme="light"] table { background: white; } 

/my/theme/theme.js


 document.documentElement.setAttribute( 'my_theme' , ( new Date().getHours() + 15 ) % 24 < 12 ? 'light' : 'dark' , ) 

Mit dieser Technik können Sie übrigens Ihr Modernizr implementieren, jedoch ohne 300 Prüfungen, die Sie nicht benötigen, da nur die Prüfungen, von denen Ihr CSS wirklich abhängt, in das Bundle aufgenommen werden.


Viele Bibliotheken


In der Regel ist der Einstiegspunkt für die Erstellung eines Bundles eine Art Datei. Im Fall von Webpack ist dies JS. Wenn Sie viele veräußerliche Bibliotheken und Anwendungen entwickeln, benötigen Sie viele Bundles. Und für jedes Bundle müssen Sie einen separaten Einstiegspunkt erstellen. Bei Parcel ist der Einstiegspunkt HTML, das für Anwendungen ohnehin erstellt werden muss. Aber für Bibliotheken ist dies irgendwie nicht sehr geeignet.


Idee: Was ist, wenn ein Modul ohne vorherige Vorbereitung zu einem unabhängigen Bündel zusammengesetzt werden kann?


Stellen wir die neueste Version des MAM-Projekt-Builders $ mol_build zusammen:


 mam mol/build 

Führen Sie nun diesen Sammler und lassen Sie ihn sich wieder zusammensetzen, um sicherzustellen, dass er sich noch zusammenbauen kann:


 node mol/build/-/node.js mol/build 

Obwohl, nein, bitten wir ihn, zusammen mit der Baugruppe Tests durchzuführen:


 node mol/build/-/node.test.js mol/build 

Und wenn alles gut gegangen ist, veröffentlichen Sie das Ergebnis in NPM:


 npm publish mol/build/- 

Wie Sie sehen, wird beim Zusammenstellen des Moduls ein Unterverzeichnis mit dem Namen erstellt - und alle Assembly-Artefakte werden dort abgelegt. Lassen Sie uns die Dateien durchgehen, die Sie dort finden:


  • web.dep.json - alle Informationen zum Abhängigkeitsdiagramm
  • web.js - Browser- web.js
  • web.js.map - Sorsmaps für ihn
  • web.esm.js - es hat die Form eines es-Moduls
  • web.esm.js.map - und sorsmaps dafür
  • web.test.js - web.test.js
  • web.test.js.map - und für Sorsmap-Tests
  • web.d.ts - Bundle mit Typen von allem, was im Skript-Bundle enthalten ist
  • web.css - Bündel mit Stilen
  • web.css.map - und sortiert Karten dafür
  • web.test.html - Einstiegspunkt zum Ausführen von Leistungstests in einem Browser
  • web.view.tree - Deklarationen aller im Bundle view.tree enthaltenen Komponenten
  • web.locale=*.json - Bundles mit lokalisierten Texten, jedes Bundle hat ein eigenes Bundle
  • package.json - Ermöglicht die sofortige Veröffentlichung des zusammengestellten Moduls in NPM
  • node.dep.json - Alle Informationen zum Abhängigkeitsdiagramm
  • node.js - Knotenskriptpaket
  • node.js.map - sorsmaps dafür
  • node.esm.js - Es hat die Form eines es-Moduls
  • node.esm.js.map - und sorsmaps dafür
  • node.test.js - das gleiche Bundle, aber auch mit Tests
  • node.test.js.map - und sorsmaps dafür
  • node.d.ts - Bundle mit Typen von allem, was im Skript-Bundle enthalten ist
  • node.view.tree - Deklarationen aller im Bundle view.tree enthaltenen Komponenten
  • node.locale=*.json - Bundles mit lokalisierten Texten, jedes Bundle hat ein eigenes Bundle

Die Statik wird einfach zusammen mit den Pfaden kopiert. Nehmen Sie als Beispiel eine Anwendung, die ihre eigenen Quellcodes anzeigt . Seine Quellen sind hier:


  • /mol/app/quine/quine.view.tree
  • /mol/app/quine/quine.view.ts
  • /mol/app/quine/index.html
  • /mol/app/quine/quine.locale=ru.json

Leider kann der Collector im Allgemeinen nicht wissen, dass wir diese Dateien zur Laufzeit benötigen. Aber wir können ihm das sagen, indem wir eine spezielle Datei in die Nähe legen:


/mol/app/quine/quine.meta.tree


 deploy \/mol/app/quine/quine.view.tree deploy \/mol/app/quine/quine.view.ts deploy \/mol/app/quine/index.html deploy \/mol/app/quine/quine.locale=ru.json 

Als Ergebnis der Assembly /mol/app/quine werden sie auf folgende Weise kopiert:


  • /mol/app/quine/-/mol/app/quine/quine.view.tree
  • /mol/app/quine/-/mol/app/quine/quine.view.ts
  • /mol/app/quine/-/mol/app/quine/index.html
  • /mol/app/quine/-/mol/app/quine/quine.locale=ru.json

Jetzt kann das Verzeichnis /mol/app/quine/- auf jedem statischen Hosting angelegt werden und die Anwendung ist voll funktionsfähig.


Zielplattformen


JS kann sowohl auf dem Client als auch auf dem Server ausgeführt werden. Und wie cool es ist, wenn Sie einen Code schreiben können und er überall funktioniert. Manchmal unterscheidet sich die Implementierung derselben Sache auf dem Client und dem Server jedoch grundlegend. Und ich möchte zum Beispiel, dass eine Implementierung für einen Knoten und eine andere für einen Browser verwendet wird.


Idee: Was ist, wenn sich der Zweck der Datei in ihrem Namen widerspiegelt?


MAM verwendet ein Tag-System in Dateinamen. $mol_state_arg Modul $mol_state_arg bietet beispielsweise Zugriff auf benutzerdefinierte Anwendungsparameter. Im Browser werden diese Parameter über die Adressleiste eingestellt. Und im Knoten über Befehlszeilenargumente. $mol_sate_arg abstrahiert den Rest der Anwendung von diesen Nuancen, indem beide Optionen mit einer einzigen Schnittstelle implementiert und in Dateien abgelegt werden:


  • / mol / state / arg / arg. web .ts - Implementierung für Browser
  • / mol / state / arg / arg. node .ts - Implementierung für einen Knoten

Quellen, die nicht mit diesen Tags versehen sind, werden unabhängig von der Zielplattform eingeschlossen.


Eine ähnliche Situation wird bei Tests beobachtet - sie möchten neben den übrigen Quellen gespeichert werden, möchten jedoch nicht in das Paket aufgenommen werden, das an den Endbenutzer geht. Daher sind Tests auch mit einem separaten Tag gekennzeichnet:


  • / mol / state / arg / arg. test .ts - Modultests, sie fallen in das Testpaket

Tags können parametrisch sein. Zum Beispiel können mit jedem Modul Texte in verschiedenen Sprachen kommen und sie sollten in den entsprechenden Sprachpaketen enthalten sein. Eine Textdatei ist ein reguläres JSON-Wörterbuch mit dem Gebietsschema im Namen:


  • / mol / app / leben / leben. locale = ru .json - texte für die russische sprache
  • / mol / app / leben / leben. locale = jp .json - Texte für Japanisch

Was ist schließlich, wenn wir Dateien in der Nähe ablegen möchten, der Kollektor sie jedoch ignorieren und nicht automatisch in das Bundle aufnehmen soll? Es reicht aus, am Anfang ihres Namens ein nicht alphanumerisches Zeichen hinzuzufügen. Zum Beispiel:


  • / hyoo / spielzeug / . git - beginnt mit einem Punkt, sodass der Collector dieses Verzeichnis ignoriert

Versionierung


Google hat AngularJS zuerst veröffentlicht und in NPM als Angular veröffentlicht. Dann schuf er ein völlig neues Framework mit einem ähnlichen Namen - Angular - und veröffentlichte es unter demselben Namen, aber bereits Version 2. Jetzt entwickeln sich diese beiden Feuerwerke unabhängig voneinander. Zwischen den Hauptversionen tritt nur eine API-brechende Änderung auf. Und der andere - zwischen den Minderjährigen . Und da es unmöglich ist, zwei Versionen derselben Abhängigkeit auf dieselbe Ebene zu bringen, kann von einem reibungslosen Übergang keine Rede sein, wenn zwei Versionen der Bibliothek für einige Zeit gleichzeitig in der Anwendung vorhanden sind.


Es scheint, dass das Team von Angular bereits auf alle möglichen Rechen getreten ist. Und noch etwas: Der Framework-Code ist in mehrere große Module unterteilt. Zuerst haben sie sie unabhängig versioniert, aber selbst sie waren sehr schnell verwirrt, welche Versionen der Module miteinander kompatibel sind, geschweige denn gewöhnliche Entwickler. Daher wechselte Angular zur End-to-End-Versionierung , bei der sich die Hauptversion des Moduls auch ohne Änderung des Codes ändern kann. Die Unterstützung mehrerer Versionen mehrerer Module ist sowohl für die Betreuer selbst als auch für das gesamte Ökosystem ein großes Problem. Schließlich werden viele Ressourcen aller Community-Mitglieder aufgewendet, um die Kompatibilität mit bereits veralteten Modulen sicherzustellen.


Die schöne Idee der semantischen Versionierung zerfällt in die harte Realität - Sie wissen nie, ob etwas kaputt geht, wenn Sie die Nebenversion oder sogar die Version des Patches ändern. Daher ist in vielen Projekten eine bestimmte Version der Abhängigkeit festgelegt. Ein solcher Fix wirkt sich jedoch nicht auf transitive Abhängigkeiten aus, die bei der Installation von Grund auf auf die neueste Version zurückgesetzt werden, aber möglicherweise gleich bleiben, wenn sie bereits installiert sind. Dieses Durcheinander führt dazu, dass Sie sich niemals auf eine feste Version verlassen können und regelmäßig die Kompatibilität mit aktuellen Versionen von (zumindest transitiven) Abhängigkeiten überprüfen müssen.


Aber was ist mit Sperrdateien ? Wenn Sie eine Bibliothek entwickeln, die über Abhängigkeiten installiert wird, hilft Ihnen die Sperrdatei nicht weiter, da sie vom Paketmanager ignoriert wird. Für die endgültige Anwendung gibt Ihnen die Sperrdatei die sogenannte "Reproduzierbarkeit von Baugruppen". Aber seien wir ehrlich. Wie oft müssen Sie die endgültige Anwendung aus derselben Quelle erstellen? Genau einmal. Empfangen der Ausgabe, unabhängig von einem NPM, des Assembly-Artefakts: eine ausführbare Binärdatei, ein Docker-Container oder nur ein Archiv mit dem gesamten Code, der zum Ausführen erforderlich ist. Ich hoffe du machst keine npm install auf prod?


Einige verwenden Sperrdateien, damit der CI-Server genau das zusammenstellt, was der Entwickler festgelegt hat. Aber warten Sie, der Entwickler selbst kann es einfach auf seinem lokalen Computer zusammenbauen. , , , . Continuous Integration , , , , - . CI , .


, , . , Angular@4 ( 3). , , " " " ". Angular@4 , Angular@5. Angular@6, . Angular TypeScript . . , 2 , … , business value , , , , .


, , , , 2 . : , — , — . 3 React, 5 jQuery, 7 lodash.


: — ?


. - . , . , . , . , . , . , , . : issue, , workaround, pull request, , . , , . . .


, . , , . . . : , , -. - — . , , - . , , , NPM . , . .


, ? — . mobx , mobx2 API . — , : , . mobx mobx2 , API. API, .


. — . , :


 var pages_count = $mol_atom2_sync( ()=> $lib_pdfjs.getDocument( uri ).promise ).document().numPages 

mol_atom2_sync lib_pdfjs , :


 npm install mol_atom2_sync@2.1 lib_pdfjs@5.6 

, , — , . ? — , *.meta.tree , :


/.meta.tree


 pack node git \https://github.com/nin-jin/pms-node.git pack mol git \https://github.com/eigenmethod/mol.git pack lib git \https://github.com/eigenmethod/mam-lib.git 

. .


NPM


MAM — NPM . , — . , , NPM .


NPM , $node. , - -:


/my/app/app.ts


 $node.portastic.find({ min : 8080 , max : 8100 , retrieve : 1 }).then( ( ports : number[] ) => { $node.express().listen( ports[0] ) }) 

, . - lib NPM . , NPM- pdfjs-dist :


/lib/pdfjs/pdfjs.ts


 namespace $ { export let $lib_pdfjs : typeof import( 'pdfjs-dist' ) = require( 'pdfjs-dist/build/pdf.min.js' ) $lib_pdfjs.disableRange = true $lib_pdfjs.GlobalWorkerOptions.workerSrc = '-/node_modules/pdfjs-dist/build/pdf.worker.min.js' } 

/lib/pdfjs/pdfjs.meta.tree


 deploy \/node_modules/pdfjs-dist/build/pdf.worker.min.js 

, .



. create-react-app angular-cli , . , , eject . . , , .


: ?


MAM . .


MAM MAM , :


 git clone https://github.com/eigenmethod/mam.git ./mam && cd mam npm install npm start 

8080 . , — MAM.


( — acme ) ( — hello home ):


/acme/acme.meta.tree


 pack hello git \https://github.com/acme/hello.git pack home git \https://github.com/acme/home.git 

npm start :


 npm start acme/hello acme/home 

. — . , , . — : https://t.me/mam_mol

Source: https://habr.com/ru/post/de456288/


All Articles