Typescript: Unterstützen Sie "mittelgroße" Projekte

Erstellt am 10. Juni 2015  ·  147Kommentare  ·  Quelle: microsoft/TypeScript

Das Anhören von @nycdotnet hat mich angefeuert, dieses anzugehen. Danke, Steve. (Übrigens kannst du dir sein gutes Interview hier ansehen: http://www.dotnetrocks.com/default.aspx?showNum=1149)

Der Vorschlag hier wurde erstmals in prähistorischer Zeit (noch vor #11) begonnen, als Dinosaurier auf der verbrannten Erde wandelten. Obwohl nichts an diesem Vorschlag per se neu ist, glaube ich, dass es höchste Zeit ist, das Thema anzugehen. Steves eigener Vorschlag lautet #3394.

Das Problem

Derzeit ist es in TypeScript ziemlich einfach, anzufangen und loszulegen, und wir machen es mit jedem Tag einfacher (mit Hilfe von Dingen wie #2338 und der Arbeit an System.js). Das ist wunderbar. Aber es gibt eine kleine Hürde, wenn die Projektgröße wächst. Wir haben derzeit ein mentales Modell, das ungefähr so ​​​​lautet:

  • Kleine Projekte: Verwenden Sie tsconfig.json, halten Sie den größten Teil Ihrer Quelle im aktuellen Verzeichnis
  • Große Projekte: Verwenden Sie benutzerdefinierte Builds, legen Sie die Quelle dort ab, wo Sie sie benötigen

Für kleine Projekte bietet Ihnen tsconfig.json eine einfach einzurichtende Möglichkeit, mit jedem der Editoren plattformübergreifend zu arbeiten. Bei Großprojekten werden Sie aufgrund der unterschiedlichen Anforderungen von Großprojekten wahrscheinlich zu Build-Systemen wechseln die Vielfalt der Bausysteme und Optionen.

Steve weist in seinem Interview darauf hin, dass dies nicht ganz das richtige Modell der Welt ist, und ich neige dazu, ihm zuzustimmen. Stattdessen gibt es drei Projektgrößen:

  • Kleine Projekte: Verwenden Sie tsconfig.json, halten Sie den größten Teil Ihrer Quelle im aktuellen Verzeichnis
  • Mittelgroße Projekte: solche mit Standard-Builds und gemeinsam genutzten Komponenten
  • Große Projekte: Verwenden Sie benutzerdefinierte Builds, legen Sie die Quelle dort ab, wo Sie sie benötigen

Wenn Sie die Größe des Projekts skalieren, müssen Sie in der Lage sein, bis zum mittleren Schritt zu skalieren, oder die Werkzeugunterstützung fällt zu schnell ab, argumentiert Steve.

Vorschlag

Um dies zu lösen, schlage ich vor, dass wir "mittelgroße" Projekte unterstützen. Diese Projekte verfügen über Standarderstellungsschritte, die heute in tsconfig.json beschrieben werden könnten, mit der Ausnahme, dass das Projekt aus mehreren Komponenten erstellt wird. Die Hypothese hier ist, dass es auf dieser Ebene eine ganze Reihe von Projekten gibt, die durch diese Unterstützung gut bedient werden könnten.

Ziele

Bieten Sie Entwicklern eine benutzerfreundliche Erfahrung, die "mittelgroße" Projekte sowohl für die Befehlszeilenkompilierung als auch für die Arbeit mit diesen Projekten in einer IDE erstellen.

Nicht-Tore

Dieser Vorschlag beinhaltet _keine_ optionale Kompilierung oder Schritte außerhalb dessen, was der Compiler heute verarbeitet. Dieser Vorschlag umfasst auch nicht die Bündelung oder Verpackung, die in einem separaten Vorschlag behandelt werden. Kurz gesagt deckt dieser Vorschlag, wie der Name schon sagt, nur die „mittelgroßen“ Projekte ab und nicht den Bedarf von Großprojekten.

Entwurf

Um mittelgroße Projekte zu unterstützen, konzentrieren wir uns auf den Anwendungsfall, in dem eine tsconfig.json auf eine andere verweist.

Beispiel tsconfig.json von heute:

{
    "compilerOptions": {
        "module": "commonjs",
        "noImplicitAny": true,
        "sourceMap": true
    },
    "files": [
        "core.ts",
        "sys.ts"
    ]
}

Vorgeschlagener tsconfig.json-Abhängigkeitsabschnitt:

{
    "compilerOptions": {
        "module": "commonjs",
        "noImplicitAny": true,
        "sourceMap": true
    },
    "dependencies": [
        "../common", 
        "../util"
    ],
    "files": [
        "core.ts",
        "sys.ts"
    ]
}

Abhängigkeiten weisen entweder auf:

  • Ein Verzeichnis, in dem eine tsconfig.json zu finden ist
  • Eine tsconfig.json direkt

Abhängigkeiten sind hierarchisch. Um das vollständige Projekt zu bearbeiten, müssen Sie das richtige Verzeichnis öffnen, das die Root-Datei tsconfig.json enthält. Dies impliziert, dass Abhängigkeiten nicht zyklisch sein können. Während es in einigen Fällen möglich sein kann, zyklische Abhängigkeiten zu behandeln, ist es in anderen Fällen, insbesondere bei Typen mit zirkulären Abhängigkeiten, möglicherweise nicht möglich, eine vollständige Auflösung durchzuführen.

Wie es funktioniert

Abhängigkeiten werden zuerst erstellt, in der Reihenfolge, in der sie im Abschnitt 'Abhängigkeiten' aufgelistet sind. Wenn eine Abhängigkeit nicht erstellt werden kann, wird der Compiler mit einem Fehler beendet und der Rest des Projekts wird nicht weiter erstellt.

Wenn jede Abhängigkeit abgeschlossen ist, wird dem aktuellen Build eine '.d.ts'-Datei zur Verfügung gestellt, die die Ausgaben darstellt. Sobald alle Abhängigkeiten abgeschlossen sind, wird das aktuelle Projekt erstellt.

Wenn der Benutzer ein Unterverzeichnis als Abhängigkeit angibt und auch dessen Kompilierung impliziert, indem er keinen 'files'-Abschnitt bereitstellt, wird die Abhängigkeit während der Abhängigkeitskompilierung kompiliert und auch aus der Kompilierung des aktuellen Projekts entfernt.

Der Sprachdienst kann jede Abhängigkeit einsehen. Da jede Abhängigkeit von ihrer eigenen tsconfig.json gesteuert wird, kann dies bedeuten, dass mehrere Sprachdienstinstanzen erstellt werden müssen. Das Endergebnis wäre ein koordinierter Sprachdienst, der in der Lage ist, Refactoring, Codenavigation, alle Referenzen usw. über Abhängigkeiten hinweg zu finden.

Einschränkungen

Das Hinzufügen eines Verzeichnisses als Abhängigkeit ohne tsconfig.json wird als Fehler betrachtet.

Es wird davon ausgegangen, dass die Ausgaben von Abhängigkeiten in sich abgeschlossen und vom aktuellen Projekt getrennt sind. Dies bedeutet, dass Sie die ausgegebenen .js einer Abhängigkeit nicht über tsconfig.json mit dem aktuellen Projekt verketten können. Externe Tools können diese Funktionalität natürlich bereitstellen.

Wie bereits erwähnt, gelten zirkuläre Abhängigkeiten als Fehler. Im einfachen Fall:

A - B
\ C

A ist das 'aktuelle Projekt' und hängt von zwei Abhängigkeiten ab: B und C. Wenn B und C selbst keine Abhängigkeiten haben, ist dieser Fall trivial. Wenn C von B abhängt, wird B C zur Verfügung gestellt. Dies gilt nicht als zirkulär. Wenn B jedoch von A abhängt, gilt dies als zirkulär und wäre ein Fehler.

Wenn im Beispiel B von C abhängt und C in sich geschlossen ist, wird dies nicht als Kreislauf betrachtet. In diesem Fall wäre die Kompilierungsreihenfolge C, B, A, was der Logik folgt, die wir für ///ref haben.

Optionale Optimierungen/Verbesserungen

Wenn eine Abhängigkeit nicht neu erstellt werden soll, wird ihr Build-Schritt übersprungen und die '.d.ts'-Darstellung aus dem vorherigen Build wird wiederverwendet. Dies könnte erweitert werden, um zu handhaben, wenn die Kompilierung von Abhängigkeiten Abhängigkeiten aufgebaut hat, die später in der 'Abhängigkeiten'-Liste des aktuellen Projekts angezeigt werden (wie im Beispiel im Abschnitt Einschränkungen).

Anstatt übergebene Verzeichnisse als Abhängigkeiten ohne tsconfig.json als Fehlerfälle zu behandeln, könnten wir optional die Standard-'Dateien' und die Einstellungen des aktuellen Projekts auf diese Abhängigkeit anwenden.

Committed Monorepos & Cross-Project References Suggestion

Hilfreichster Kommentar

Docs/Blogpost in Arbeit unten (wird dies basierend auf Feedback bearbeiten)

Ich würde jedem empfehlen, der diesem Thread folgt, es auszuprobieren. Ich arbeite jetzt an dem Monorepo-Szenario, um die letzten Fehler / Funktionen dort auszubügeln und sollte bald eine Anleitung dazu haben


Projektreferenzen

Projektreferenzen sind eine neue Funktion in TypeScript 3.0, mit der Sie Ihre TypeScript-Programme in kleinere Teile strukturieren können.

Auf diese Weise können Sie die Buildzeiten erheblich verkürzen, eine logische Trennung zwischen Komponenten erzwingen und Ihren Code auf neue und bessere Weise organisieren.

Wir führen auch einen neuen Modus für tsc , das --build Flag, das Hand in Hand mit Projektreferenzen arbeitet, um schnellere TypeScript-Builds zu ermöglichen.

Ein Beispielprojekt

Schauen wir uns ein ziemlich normales Programm an und sehen, wie Projektreferenzen uns helfen können, es besser zu organisieren.
Stellen Sie sich vor, Sie haben ein Projekt mit zwei Modulen, converter und units und jeweils einer entsprechenden Testdatei:

/src/converter.ts
/src/units.ts
/test/converter-tests.ts
/test/units-tests.ts
/tsconfig.json

Die Testdateien importieren die Implementierungsdateien und führen einige Tests durch:

// converter-tests.ts
import * as converter from "../converter";

assert.areEqual(converter.celsiusToFahrenheit(0), 32);

Zuvor war diese Struktur ziemlich umständlich, wenn Sie eine einzelne tsconfig-Datei verwendet haben:

  • Es war möglich, dass die Implementierungsdateien die Testdateien importieren
  • Es war nicht möglich, test und src gleichzeitig zu erstellen, ohne dass src im Ausgabeordnernamen angezeigt wurde, was Sie wahrscheinlich nicht möchten
  • Das Ändern nur der Interna in den Implementierungsdateien erforderte eine erneute Typprüfung der Tests, obwohl dies nie zu neuen Fehlern führen würde
  • Das Ändern nur der Tests erforderte eine erneute Typprüfung der Implementierung, auch wenn sich nichts geändert hat

Sie könnten mehrere tsconfig-Dateien verwenden, um einige dieser Probleme zu lösen, aber es würden neue erscheinen:

  • Es gibt keine integrierte aktuelle Überprüfung, sodass Sie am Ende immer tsc zweimal ausführen
  • Das zweimalige Aufrufen von tsc verursacht mehr Startzeit-Overhead
  • tsc -w kann nicht auf mehreren Konfigurationsdateien gleichzeitig ausgeführt werden

Projektreferenzen können all diese Probleme und mehr lösen.

Was ist eine Projektreferenz?

tsconfig.json Dateien haben eine neue Top-Level-Eigenschaft, references . Es ist ein Array von Objekten, das Projekte angibt, auf die verwiesen werden soll:

{
    "compilerOptions": {
        // The usual
    },
    "references": [
        { "path": "../src" }
    ]
}

Die path Eigenschaft jeder Referenz kann auf ein Verzeichnis verweisen, das eine tsconfig.json Datei enthält, oder auf die Konfigurationsdatei selbst (die einen beliebigen Namen haben kann).

Wenn Sie auf ein Projekt verweisen, passieren neue Dinge:

  • Beim Importieren von Modulen aus einem referenzierten Projekt wird stattdessen dessen Ausgabedeklarationsdatei geladen ( .d.ts )
  • Wenn das referenzierte Projekt ein outFile , sind die Deklarationen der Ausgabedatei .d.ts in diesem Projekt sichtbar
  • Der Build-Modus (siehe unten) erstellt das referenzierte Projekt bei Bedarf automatisch

Durch die Aufteilung in mehrere Projekte können Sie die Geschwindigkeit der Typprüfung und Kompilierung erheblich verbessern, den Speicherverbrauch bei der Verwendung eines Editors reduzieren und die Durchsetzung der logischen Gruppierungen Ihres Programms verbessern.

composite

Für referenzierte Projekte muss die neue Einstellung composite aktiviert sein.
Diese Einstellung ist erforderlich, um sicherzustellen, dass TypeScript schnell feststellen kann, wo die Ausgaben des referenzierten Projekts zu finden sind.
Das Aktivieren des Flags composite ändert einige Dinge:

  • Die Einstellung rootDir , wenn sie nicht explizit festgelegt wurde, standardmäßig das Verzeichnis, das die Datei tsconfig enthält
  • Alle Implementierungsdateien müssen mit einem include Muster übereinstimmen oder im files Array aufgeführt sein. Wenn diese Einschränkung verletzt wird, informiert Sie tsc , welche Dateien nicht angegeben wurden
  • declaration muss aktiviert sein

declarationMaps

Wir haben auch Unterstützung für Deklarationsquellzuordnungen hinzugefügt.
Wenn Sie --declarationMap aktivieren, können Sie Editorfunktionen wie "Gehe zu Definition" und Umbenennen verwenden, um in unterstützten Editoren transparent über Projektgrenzen hinweg zu navigieren und Code zu bearbeiten.

prepend mit outFile

Sie können das Voranstellen der Ausgabe einer Abhängigkeit auch mit der Option prepend in einer Referenz aktivieren:

   "references": [
       { "path": "../utils", "prepend": true }
   ]

Wenn Sie ein Projekt voranstellen, wird die Ausgabe des Projekts über die Ausgabe des aktuellen Projekts eingefügt.
Dies funktioniert sowohl für .js Dateien als auch für .d.ts Dateien, und auch Quellzuordnungsdateien werden korrekt ausgegeben.

tsc verwendet für diesen Vorgang immer nur vorhandene Dateien auf der Festplatte, daher ist es möglich, ein Projekt zu erstellen, bei dem keine korrekte Ausgabedatei generiert werden kann, da die Ausgabe einiger Projekte in der resultierenden Datei mehr als einmal vorhanden wäre .
Zum Beispiel:

  ^ ^ 
 /   \
B     C
 ^   ^
  \ /
   D

In dieser Situation ist es wichtig, nicht jeder Referenz voranzustellen, da Sie am Ende zwei Kopien von A in der Ausgabe von D - dies kann zu unerwarteten Ergebnissen führen.

Vorbehalte für Projektreferenzen

Projektreferenzen haben einige Kompromisse, die Sie beachten sollten.

Da abhängige Projekte .d.ts Dateien verwenden, die aus ihren Abhängigkeiten erstellt wurden, müssen Sie entweder bestimmte Build-Ausgaben einchecken oder ein Projekt nach dem Klonen erstellen, bevor Sie in einem Editor durch das Projekt navigieren können, ohne störende Fehler zu sehen Fehler.
Wir arbeiten an einem .d.ts-Generierungsprozess hinter den Kulissen, der in der Lage sein sollte, dies zu mildern, aber vorerst empfehlen wir, Entwickler zu informieren, dass sie nach dem Klonen erstellen sollten.

Um die Kompatibilität mit bestehenden Build-Workflows zu wahren, erstellt tsc nicht automatisch Abhängigkeiten, es sei denn, es wird mit dem Schalter --build aufgerufen.
Lassen Sie uns mehr über --build erfahren.

Build-Modus für TypeScript

Eine lang erwartete Funktion sind intelligente inkrementelle Builds für TypeScript-Projekte.
In 3.0 können Sie das Flag --build mit tsc .
Dies ist praktisch ein neuer Einstiegspunkt für tsc , der sich eher wie ein Build-Orchestrator als wie ein einfacher Compiler verhält.

Das Ausführen von tsc --build (kurz tsc -b ) bewirkt Folgendes:

  • Finden Sie alle referenzierten Projekte
  • Erkennen, ob sie auf dem neuesten Stand sind
  • Bauen Sie veraltete Projekte in der richtigen Reihenfolge auf

Sie können tsc -b mit mehreren Konfigurationsdateipfaden versehen (zB tsc -b src test ).
Genau wie bei tsc -p ist die Angabe des Konfigurationsdateinamens selbst unnötig, wenn er tsconfig.json .

tsc -b Befehlszeile

Sie können eine beliebige Anzahl von Konfigurationsdateien angeben:

 > tsc -b                                # Build the tsconfig.json in the current directory
 > tsc -b src                            # Build src/tsconfig.json
 > tsc -b foo/release.tsconfig.json bar  # Build foo/release.tsconfig.json and bar/tsconfig.json

Machen Sie sich keine Sorgen um die Reihenfolge der Dateien, die Sie auf der Befehlszeile übergeben - tsc ordnet sie bei Bedarf neu an, sodass Abhängigkeiten immer zuerst erstellt werden.

Es gibt auch einige Flags, die spezifisch für tsc -b :

  • --verbose : Gibt eine ausführliche Protokollierung aus, um zu erklären, was vor sich geht (kann mit jedem anderen Flag kombiniert werden)
  • --dry : Zeigt an, was getan werden würde, erstellt aber nichts
  • --clean : Löscht die Ausgaben der angegebenen Projekte (kann mit --dry kombiniert werden)
  • --force : Verhalten Sie sich so, als ob alle Projekte veraltet wären
  • --watch : Watch-Modus (kann nicht mit anderen Flags außer --verbose kombiniert werden)

Vorbehalte

Normalerweise erzeugt tsc bei Syntax- oder Typfehlern Ausgaben ( .js und .d.ts ), es sei denn, noEmitOnError ist aktiviert.
Dies in einem inkrementellen Build-System zu tun, wäre sehr schlecht - wenn eine Ihrer veralteten Abhängigkeiten einen neuen Fehler hätte, würden Sie ihn nur einmal sehen, da ein nachfolgender Build das Erstellen des jetzt aktuellen Projekts überspringen würde.
Aus diesem Grund verhält sich tsc -b effektiv so, als ob noEmitOnError für alle Projekte aktiviert wäre.

Wenn Sie Build-Ausgaben einchecken ( .js , .d.ts , .d.ts.map usw.), müssen Sie möglicherweise nach einer bestimmten Quellcodeverwaltung einen --force Build ausführen abhängig davon, ob Ihr Quellcodeverwaltungstool Zeittabellen zwischen der lokalen Kopie und der Remotekopie beibehält.

msbuild

Wenn Sie ein msbuild-Projekt haben, können Sie den Build-Modus aktivieren, indem Sie hinzufügen

    <TypeScriptBuildMode>true</TypeScriptBuildMode>

in Ihre proj-Datei. Dies ermöglicht sowohl den automatischen inkrementellen Build als auch die Bereinigung.

Beachten Sie, dass wie bei tsconfig.json / -p vorhandene TypeScript-Projekteigenschaften nicht berücksichtigt werden - alle Einstellungen sollten mit Ihrer tsconfig-Datei verwaltet werden.

Einige Teams haben msbuild-basierte Workflows eingerichtet, bei denen tsconfig-Dateien dieselbe implizite Diagrammreihenfolge aufweisen wie die verwalteten Projekte, mit denen sie gepaart sind.
Wenn Ihre Lösung so ist, können Sie weiterhin msbuild mit tsc -p zusammen mit Projektreferenzen verwenden; diese sind vollständig interoperabel.

Orientierungshilfe

Gesamtstruktur

Bei mehr tsconfig.json Dateien möchten Sie normalerweise die Vererbung von
Auf diese Weise können Sie eine Einstellung in einer Datei ändern, anstatt mehrere Dateien bearbeiten zu müssen.

Eine weitere bewährte Methode ist es, eine "Lösungsdatei" tsconfig.json , die einfach references für alle Ihre Blattknotenprojekte enthält.
Dies stellt einen einfachen Einstiegspunkt dar; zB führen wir im TypeScript-Repository einfach tsc -b src , um alle Endpunkte zu erstellen, da wir alle Unterprojekte in src/tsconfig.json
Beachten Sie, dass es ab 3.0 kein Fehler mehr ist, ein leeres files Array zu haben, wenn Sie mindestens ein reference in einer tsconfig.json Datei haben.

Sie können diese Muster im TypeScript-Repository sehen - siehe src/tsconfig_base.json , src/tsconfig.json und src/tsc/tsconfig.json als wichtige Beispiele.

Strukturierung für relative Module

Im Allgemeinen ist nicht viel erforderlich, um ein Repo mit relativen Modulen zu übertragen.
Platzieren Sie einfach eine tsconfig.json Datei in jedem Unterverzeichnis eines gegebenen übergeordneten Ordners und fügen Sie reference s zu diesen Konfigurationsdateien hinzu, um der beabsichtigten Schichtung des Programms zu entsprechen.
Sie müssen entweder outDir auf einen expliziten Unterordner des Ausgabeordners setzen oder rootDir auf das gemeinsame Stammverzeichnis aller Projektordner setzen.

Strukturierung für outFiles

Das Layout für Kompilierungen mit outFile ist flexibler, da relative Pfade nicht so wichtig sind.
Beachten Sie, dass Sie im Allgemeinen prepend bis zum "letzten" Projekt nicht verwenden möchten - dies verbessert die Build-Zeiten und reduziert die Menge an E/A, die in einem bestimmten Build erforderlich ist.
Das TypeScript-Repository selbst ist hier eine gute Referenz - wir haben einige "Bibliotheks"-Projekte und einige "Endpunkt"-Projekte; "Endpoint"-Projekte werden so klein wie möglich gehalten und ziehen nur die Bibliotheken ein, die sie benötigen.

Strukturierung für Monorepos

TODO: Experimentiere mehr und finde das heraus. Rush und Lerna scheinen unterschiedliche Modelle zu haben, die auf unserer Seite unterschiedliche Dinge implizieren

Alle 147 Kommentare

Du meine Güte!

:+1:

Ja! Dies ist für die von Ihnen bereitgestellten Anwendungsfälle durchaus sinnvoll. Es ist definitiv das Richtige, den von uns verwendeten Tools einen besseren Überblick darüber zu geben, wie unser Code verwendet werden soll. Jedes Mal, wenn ich aus Versehen mit F12 in eine .d.ts-Datei gehe, habe ich das Gefühl, ein Kätzchen zu erwürgen!

Jonathan,

Vielen Dank für das nette Feedback und die Entscheidung, dies zu übernehmen. TypeScript ist so ein großartiges Werkzeug und diese Funktionalität wird vielen Leuten helfen, die ihre mittelgroßen Codebasen komponentisieren wollen, die die Ineffizienz, die durch riesige Projekte erforderlich ist, die auf strikter Trennung der Belange angewiesen sind, nicht rechtfertigen können (zum Beispiel die Azure-Portale oder das Projekt Monacos von die Welt mit > 100kloc und vielen unabhängigen Teams). Mit anderen Worten, dies wird "normalen Leuten" wirklich helfen. Außerdem haben andere bereits Material dafür vorgeschlagen, zum Beispiel

Ich finde Ihren Vorschlag hervorragend. Das einzige Manko, das ich gegenüber meinem Vorschlag (#3394), den ich jetzt geschlossen habe, sehe, ist das Fehlen eines Fallback-Mechanismus für Referenzen.

Betrachten Sie das folgende reale Szenario, das ich hier beschrieben habe: https://github.com/Microsoft/TypeScript/issues/3394#issuecomment -109359701

Ich habe ein TypeScript-Projekt grunt-ts, das von einem anderen Projekt csproj2ts abhängt. Kaum jemand, der an grunt-ts arbeitet, wird auch an csproj2ts arbeiten wollen, da es einen sehr eingeschränkten Funktionsumfang hat. Für jemanden wie mich wäre es jedoch großartig, an beiden Projekten gleichzeitig zu arbeiten und Refactoring durchzuführen / zur Definition zu gehen / alle Referenzen zu finden.

Als ich meinen Vorschlag machte, schlug ich vor, dass das Abhängigkeitsobjekt ein Objektliteral mit benannten Fallbacks ist. Eine Version, die Ihrem Vorschlag besser entspricht, wäre:

"dependencies": {
   "csproj2ts": ["../csproj2ts","node_modules/csproj2ts/csproj2ts.d.ts"],
   "SomeRequiredLibrary": "../SomeRequiredLibraryWithNoFallback"
}

Um es so zu vereinfachen, dass es immer noch ein Array ist, schlage ich die folgende alternative Implementierung eines zukünftigen hypothetischen dependencies Abschnitts der grunt-ts tsconfig.json Datei vor:

"dependencies": [
   ["../csproj2ts","node_modules/csproj2ts/csproj2ts.d.ts"],
   "../SomeRequiredLibraryWithNoFallback"
]

Die Auflösungsregel für jedes Array-Typ-Element in dependencies wäre: Das _erste_ Element, das in jedem gefunden wird, wird verwendet, und der Rest wird ignoriert. Elemente vom Typ String werden genauso behandelt, wie es in Jonathans Vorschlag angegeben ist.

Dies ist eine etwas komplizierter zu implementierende Lösung, gibt dem Entwickler (und den Bibliotheksautoren) jedoch _weitaus größere_ Flexibilität. Für Entwickler, die nicht auf csproj2ts entwickeln müssen (und daher keine ../csproj2ts/tsconfig.json Datei haben), ist die Abhängigkeit nur eine Definitionsdatei, die dem Kompilierungskontext hinzugefügt wird. Für Entwickler, die eine ../csproj2ts/tsconfig.json Datei _do_ haben, funktioniert der Vorschlag genau so, wie Sie es oben beschrieben haben.

Im obigen Beispiel müsste "../SomeRequiredLibraryWithNoFallback" genau wie in Ihrem vorhandenen Vorschlag vorhanden sein, und sein Fehlen wäre ein Compilerfehler.

Vielen Dank, dass Sie dies in Betracht ziehen.

Hier gibt es zwei Probleme, die wir auseinander brechen müssen, es gibt einen Build- und einen Sprachdienst-Support.

Für Build glaube ich nicht, dass tsconfig der richtige Ort dafür ist. Dies ist eindeutig ein Problem mit dem Build-System. Bei diesem Vorschlag muss der Typoskript-Compiler in den Bereichen:

  • Abhängigkeiten herausfinden
  • aktuelle Überprüfungen von Behauptungen
  • Konfigurationsmanagement (Release vs. Debug)

Dies alles liegt eindeutig in der Verantwortung von Build-Systemen; das sind harte probleme und es gibt schon tools die das tun, zB MSBuild, grunt, gulp, etc..
Sobald tsconfig und tsc zum Build-Treiber werden, möchten Sie, dass er alle CPUs verwendet, um nicht verwandte Unterbäume zu erstellen oder Post- und Pre-Build-Befehle für jedes Projekt zu haben und möglicherweise auch andere Projekte zu erstellen. Auch hier denke ich, dass es Build-Tools gibt, die gut in dem sind, was sie tun, und wir müssen das nicht neu erstellen.

Für den Sprachdienst,
Ich denke, es ist in Ordnung, wenn Tools über mehrere tsconfig-Dateien Bescheid wissen und daraus folgern und Ihnen mehr helfen können, aber das sollte Ihren Build nicht beeinträchtigen. ich würde sowas in Betracht ziehen:

"files" : [
    "file1.ts",
    {
        "path": "../projectB/out/projectB.d.ts",
         "sourceProject": "../projectB/"
     }
]

Wo tsc nur auf "Pfad" schaut, aber Tools können andere Informationen betrachten und versuchen, so hilfreich wie möglich zu sein.

Ich erkenne die Existenz des Problems an, glaube aber nicht, dass das Zusammenlegen von Build und Werkzeugen die richtige Lösung ist. tsconfig.json sollte eine Konfigurationstasche bleiben (dh eine json-Alternative zu Antwortdateien) und kein Build-System werden. Eine tsconfig.json repräsentiert einen einzelnen tsc-Aufruf. tsc sollte ein einzelner Projekt-Compiler bleiben.

MSBuild-Projekte in VS sind ein Beispiel für die Verwendung eines Buildsystems zum Erstellen von IDE-Features, und jetzt sind ppl nicht zufrieden damit, da es zu groß ist.

Danke für deine Antwort, Mohamed. Lassen Sie mich noch einmal formulieren, um zu sehen, ob ich es verstanden habe:

  • Sie denken, dass die Aufgabe, Multiprojekt-Builds zu koordinieren, die Domäne dedizierter Build-Tools bleiben sollte.
  • Sie denken, dass an diesem Vorschlag für den TypeScript-Sprachdienst etwas dran sein könnte.
  • Sie denken, dass das Ausführen von tsc --project auf diesem tsconfig.json dasselbe wäre wie das Ausführen von tsc file1.ts ../project/out/project.d.ts . Das Öffnen eines solchen Projekts in VS oder einem anderen Editor mit dem TypeScript-Sprachdienst würde es dem Entwickler jedoch ermöglichen, "zur Definition zu gehen", um den Entwickler zu der _tatsächlichen TypeScript-Datei_ zu bringen, in der das Feature definiert wurde (anstelle der Definition in projectB.d.ts ).

Habe ich das Recht?

Wenn ja, finde ich das sehr fair. In meinem ursprünglichen Vorschlag (https://github.com/Microsoft/TypeScript/issues/3394) habe ich gesagt, dass meine Idee unvollständig war, da sie nicht den Schritt des Kopierens der ausgegebenen Ergebnisse von dort beinhaltete, wo sie ausgegeben werden würden in der referenzierten Bibliothek dorthin, wo die referenzierende Bibliothek sie zur Laufzeit erwarten würde. Ich denke, Sie sagen: "Warum auf halbem Weg zum Bauen gehen, wenn das, was wirklich benötigt wird, die Unterstützung des Sprachdienstes ist".

Um die Daten in Ihrem Beispiel ein wenig zu ändern, wären Sie bereit, so etwas zu unterstützen?

"files" : [
    "file1.ts",
    {
        "path": "externalLibraries/projectB.d.ts",
         "sourceProject": "../projectB/"
     }
]

Es wird davon ausgegangen, dass das aktuelle Projekt mit einer Definition für projectB ausgeliefert wird, die standardmäßig verwendet wird, aber wenn die tatsächliche Quelle für projectB verfügbar ist, wird stattdessen die tatsächliche Quelle verwendet.

@nycdotnet du hast es richtig zusammengefasst; Ich möchte ein System erstellen, das lose gekoppelt ist und das Mischen und Anpassen verschiedener Build-Tools mit verschiedenen IDEs ermöglicht, aber dennoch ein großartiges Design-Zeit-Erlebnis bietet.

Klingt gut!

Ich stimme @mhegazy zu und denke eigentlich, dass es wichtig ist, dass TypeScript aufhört, sich selbst als 'Compiler' zu sehen und sich selbst als 'Type-Checker' und 'Transpiler' zu sehen. Jetzt gibt es Single-File-Transpilation-Unterstützung. Ich sehe keinen Grund, kompilierte JavaScript-Dateien zu erstellen, außer zur Laufzeit/Bündelung. Ich sehe auch nicht, warum es notwendig ist, die externen Referenzdefinitionen während der Typprüfung zu generieren, wenn die eigentliche Typoskriptquelle verfügbar ist.

Die Datei- und Paketauflösung liegt in der Verantwortung des von Ihnen verwendeten Build-Systems (browserify, systemjs, webpack usw.). benutzen. Dies bedeutet entweder die Implementierung eines benutzerdefinierten LanguageServicesHost für jedes Build-System oder die Bereitstellung eines Tools für jedes einzelne, das die korrekten Zuordnungseinträge in tsconfig.json generiert. Beides ist akzeptabel.

@nycdotnet Ich denke, Ihr Anwendungsfall für mehrere Fallback-Pfade würde besser mit npm link ../csproj2ts gehandhabt werden?

Ich stimme @mhegazy zu, dass wir den Build vom Compiler/Transpiler getrennt halten sollten. Ich möchte die Abhängigkeit von tsconfig -> tsconfig in den Dateiabschnitt aufnehmen, vorausgesetzt, dass, wenn der Abschnitt keine *.ts-Dateien auflistet, immer noch nach ihnen gesucht wird. Z.B

"Dateien": [
{
"path": "externalLibraries/projectB.d.ts",
"sourceProject": "../projectB/"
}
]

Beinhaltet weiterhin alle ts-Dateien im Verzeichnis und Unterverzeichnis, die die Datei tsconfig.json enthalten.

@dbaeumer Sie möchten es dann in einer anderen Eigenschaft haben, richtig? derzeit werden Dateien, die definiert sind, immer verwendet und wir ignorieren den Include *.ts-Teil.

@mhegazy nicht unbedingt ein anderer Abschnitt, obwohl es am Ende die Dinge klarer machen würde. Alles, was ich vermeiden möchte, ist, gezwungen zu werden, alle Dateien aufzulisten, wenn ich eine tsconfig -> tsconfig-Abhängigkeit verwende. Im obigen Beispiel möchte ich noch keine *.ts-Dateien auflisten, um sie in den Compiler einzuspeisen.

Ich denke, das ist dringend nötig. Ich glaube jedoch nicht, dass wir die Build-Frage vermeiden können. Das bedeutet nicht, dass wir zusammen mit diesem Vorschlag ein Build-System implementieren müssen, aber wir hätten uns einige gute Anleitungen überlegen sollen, die in einer Konfiguration wie der vorgeschlagenen funktionieren. Es wird nicht trivial sein, es richtig zu machen (und wir müssen es für Visual Studio lösen).

Wird im obigen Vorschlag (wo sowohl auf .d.ts als auch auf die Quelle verwiesen wird) erkannt, ob die .d.ts veraltet sind (dh neu erstellt werden müssen)? Funktionieren Operationen wie Refactor/Rename projektübergreifend (dh aktualisiert den Namen in der referenzierten Projektquelle, nicht nur die .d.ts-Datei, die beim nächsten Build überschrieben wird)? Bringt mich GoToDef zum Originalcode im referenzierten Projekt (nicht mitten in einer riesigen .d.ts-Datei für das gesamte Projekt)? Dies würde bedeuten, dass die Quelle der referenzierten Projekte aufgelöst und in einigen Fällen analysiert werden muss. In welchem ​​Fall sind die .d.ts-Dateien so nützlich?

Die allgemeine Lösung, die wir heute haben, ist eine .d.ts-Datei als Build-Ausgabe eines Projekts und wird dann als Eingabe im anderen Projekt referenziert. Dies funktioniert gut für den Build, also müssen Sie das nicht ändern.

Das Problem ist das Bearbeitungsszenario. Sie möchten beim Bearbeiten nicht durch eine generierte Datei gehen. Meine vorgeschlagene Lösung besteht darin, einen "Hinweis" zu geben, woher die generierten .d.ts stammen. Der Sprachdienst lädt dann die .d.ts nicht, sondern lädt stattdessen ein "Projekt" aus dem Hinweispfad. Auf diese Weise gelangen Sie mit goto def zur Implementierungsdatei anstelle der .d.ts-Datei und ähnlich würden Fehler ohne Kompilierung funktionieren.

Operationen wie Umbenennen, werden von einem Projekt zum anderen "verbreitet", ebenso wie das Finden von Referenzen.

Heute ist es völlig Sache des Hosts (der IDE, was auch immer), die Datei tsconfig.json zu finden, obwohl TS dann APIs bereitstellt, um sie zu lesen und zu parsen. Wie würden Sie sich das vorstellen, wenn mehrere tsconfig.json hierarchisch angeordnet sind? Wäre der Host immer noch für das Auflösen der ursprünglichen Datei verantwortlich, aber nicht die anderen, oder wäre der Host für das Auflösen aller tsconfigs verantwortlich?

Es scheint, als ob es einen Kompromiss zwischen Bequemlichkeit/Konvention und Flexibilität gibt.

Würde dies nicht mit der Fähigkeit beginnen, d.ts-Dateien wie in #2568 beschrieben zu erstellen (oder zumindest relative Importe zu haben)?

@spion Ich bin mir nicht sicher, ob ich die Abhängigkeit hier sehe. Sie können mehrere Ausgaben eines Projekts haben, es muss nicht eine einzelne Auswahldatei sein. das Build-System sollte das wissen und sie als Eingaben mit abhängigen Projekten verbinden.

@mhegazy Ups , tut mir leid. Wenn man sich das Problem noch einmal ansieht, scheint es, dass dies eher mit dem Sprachdienst zusammenhängt. Ich habe folgendes gelesen

  • Mittelgroße Projekte: solche mit Standard-Builds und gemeinsam genutzten Komponenten

und ging automatisch davon aus, dass es sich um eine bessere Unterstützung für npm/browserify (oder webpack) Build-Workflows handelt, bei denen Teile des Projekts externe Module sind.

AFAIK gibt es noch keine Möglichkeit, .d.ts-Dateien für externe Module zu generieren? Wenn dies der Fall ist, könnte ein Sprachdienst Projekte, die externe Module importieren, nur mit etwas wie diesem in tsconfig.json verknüpfen:

{ 
  "provides": "external-module-name"
}

die den LS informieren würde, wenn das Projekt in einem anderen tsconfig.json referenziert wird

AFAIK gibt es noch keine Möglichkeit, .d.ts-Dateien für externe Module zu generieren?

Ich glaube nicht, dass dies stimmt. Der Aufruf von tsc --m --d erzeugt eine Deklarationsdatei, die selbst ein externes Modul ist. Auflösungslogik versucht, eine .ts zu finden und wenn nicht, dann eine .d.ts mit demselben Namen,

@spion TypeScript kann d.ts-Dateien für externe Module generieren , wie https://github.com/TypeStrong/dts-bundle

@mhegazy sorry, ich meinte für "ambient external modules", dh wenn ich external-module-name in TypeScript schreibe und eine seiner Klassen aus einem anderen Modul importiere:

import {MyClass} from 'external-module-name'

Es gibt keine Möglichkeit, tsc , die entsprechende .d.ts-Datei zu generieren, die 'external-module-name' deklariert

@nycdotnet Ich kenne dts-bundle und dts-generator, aber wenn der Sprachdienst die Quelle meines anderen Projekts kennen soll, sollte er auch wissen, welchen Modulnamen er bereitstellt, um die Importe korrekt verfolgen zu können

Wie ist der Status dieser Funktion? Dies scheint eine wichtige Option für mittelgroße Projekte zu sein. Wie konfigurieren Sie ein Projekt, das eine Quelle in verschiedenen Ordnern mit einer bestimmten "requirejs" -Konfiguration hat?

@llgcode bitte https://github.com/Microsoft/TypeScript/issues/5039 , dies sollte in typescript@next verfügbar sein.

Ich verstehe gar nicht, was daran so schwer ist, ein Gulpfile mit Aufgaben zu schreiben, die die Teilprojekte so zusammenstellen, wie man es für mittelgroße Projekte braucht. Ich mache das sogar in kleinen Projekten. Der einzige Grund, warum ich tsconfig.json überhaupt verwende, ist für VS-Code

Zuerst benutze ich keinen Schluck. Zweitens kann dies ein großes Projekt sein, bei dem Sie nicht jedes Mal alles neu kompilieren möchten. Aber wenn Sie eine gute Lösung mit gulp haben, lassen Sie mich wissen, wie das geht.

@llgcode Nun, gulp ist ein Task-Runner. Sie schreiben eine gulpfile.js, in der Sie mit gulp.task() beliebig viele Aufgaben definieren. Innerhalb Ihrer Aufgabe können Sie einen Strom von Eingabedateien mit gulp.src() und dann .pipe() durch eine Pipeline von Transformationen wie Kompilierung, Verkettung, Verkleinerung, Quellzuordnungen, Kopieren von Assets ... tun Sie alles, was mit Node- und NPM-Modulen möglich ist.
Wenn Sie mehrere Projekte kompilieren müssen, definieren Sie einfach eine Aufgabe, die dies tut. Wenn Sie mehrere tsconfig.json verwenden möchten, bietet gulp- typescript Unterstützung dafür, oder Sie können einfach die json-Dateien lesen. Inkrementelle Builds sind ebenfalls möglich. Ich weiß nicht, wie Ihr Projekt aufgebaut ist, wenn Sie sie in verschiedenen Repos haben und Submodule verwenden, oder was auch immer. Aber gulp ist 100% flexibel.

Ok danke scheint ein tolles Tool zu sein. Wenn ich eine Anforderung mit Mapping wie require("mylibs/lib") habe und meine Dateien zum Beispiel in einem Ordner project/src/lib.js sind, dann funktioniert die Vervollständigung nicht in Atom und ich weiß nicht, wie Typescript oder gulp gelöst werden die Zuordnung/Konfiguration mit "mylibs" und einem lokalen Pfad. Daher denke ich, dass diese neuen Optionspfade in #5039 eine gute Lösung für dieses Problem sind.

@llgcode Nun, mit gulp können Sie alle Dateien (einschließlich .d.ts-Dateien) mit Globs erhalten, siehe https://www.npmjs.com/package/gulp-typescript#resolving -files.

Ich denke nur, dass TypeScript hier zu viel versucht, es ist ein Transpiler und kein Build-Tool. Die Verwaltung von "Abhängigkeiten", wie sie im Vorschlag erwähnt werden, ist in Wirklichkeit die Aufgabe von Paketmanagern oder Versionskontrollsystemen und ihre Verbindung ist die Aufgabe von Build-Tools.

@felixfbecker Dem stimme ich nicht zu. Jeder mir bekannte Compiler (mit Typprüfung) hat eine solche Option. zum Beispiel:
gcc -> Dateien einschließen
java -> Klassenpfad & Quellpfad
gehen -> GOPATH
python -> PYTHONPATH
Der Compiler/Transpiler muss wissen, welche Quelldateien transpiliert werden müssen, welche Quelldateien sind nur include/lib-Dateien.
Ein Build-Tool wie gulp wird benötigt, um zu wissen, was zu tun ist, wenn sich eine Datei ändert.

Stimme mit @llgcode zu . Abgesehen von der Bereitstellung als Compiler wird TypeScript auch als Sprachdienst bereitgestellt, der Syntaxhervorhebung (tatsächliche Erkennung) und Vervollständigungsfunktionen für die IDE bereitstellt. Und DIE müssen auch den Abhängigkeitsbaum durchlaufen.

@llgcode @unional Gültige Punkte. Eine Sache, die auch helfen kann, ist, die Eigenschaft files in tsconfig.json zu veranlassen, Globs zu akzeptieren, damit Sie alle Dateien in allen Ordnern definieren können, die Sie einschließen möchten. Aber ich verstehe, woher Sie kommen und warum man für größere Projekte möglicherweise mehrere tsconfig.json verwenden möchte.

AFAIK für CommonJS wird bereits über node_modules und npm link ../path/to/other-project

Der npm-Link funktioniert nicht, sobald Sie Bibliotheken projektübergreifend wiederverwenden. Wenn Sie eine gemeinsame Bibliothek zwischen zwei eigenen eigenen Projekten verwenden (am Beispiel von rxjs), sagt Ihnen das Typescript, dass 'Observable nicht zu Observable zuordenbar ist'. Das liegt daran, dass die Include-Pfade Symlink-Ordnern zu zwei verschiedenen node_modules-Ordnern folgen und obwohl sie dieselbe Bibliothek sind. Problemumgehungen führen dazu, dass gulp-Aufgaben oder lokale/private npm-Repos erstellt werden, im Wesentlichen zurück zur Option für große Projekte.

@EricABC Das liegt wahrscheinlich daran, dass sie Umgebungsdeklarationen für externe Module verwenden. In diesem Fall sollten sie auch Definitionen für die neu unterstützten node_modules basierten .d.ts-Dateien enthalten. Ansonsten sollte es kein Problem sein, da TS-Typen nur strukturell geprüft werden, es also keine Rolle spielt, ob sie aus anderen Modulen stammen oder einen anderen Namen haben, solange die Strukturen übereinstimmen.

Danke @spion , würdest du mich vor selbst verursachten Schmerzen retten.

Eine Sache, die auch helfen kann, ist, die Dateieigenschaft in tsconfig.json zu veranlassen, Globs zu akzeptieren ...

Es gibt eine include Eigenschaft in der Diskussion

Fragen und Anmerkungen:

  • dependencies sollte den vollständigen tsconfig.json-Pfad zulassen, da
  • Warum ein neues Schlüsselwort ( dependencies ) einführen, wenn files bereits existiert und in Ordnung ist?
    Beispiel:
{
    "compilerOptions": {
        // ...
    },
    "files": [
        "../common/tsconfig.json", // <== takes the `files` part of the tsconfig.json
        "../common/tsconfig.util.json", // <==
        "core.ts",
        "sys.ts"
    ]
}
  • Was passiert, wenn die Abhängigkeit tsconfig.json auch compilerOptions angibt?


Gehen wir weiter/wild :-) und erlauben (in Zukunft) compilerOptions , exclude ... möglicherweise auf eine andere tsconfig.json zu verweisen:

// File app/tsconfig.json
{
    "compilerOptions": "../common/tsconfig.compilerOptions.json",
    "files": [
        "../common/tsconfig.json",
        "../common/tsconfig.util.json",
        "core.ts",
        "sys.ts"
    ],
    "exclude": "../common/exclude.json"
}

// File ../common/tsconfig.compilerOptions.json
{
    "compilerOptions": {
        "module": "commonjs",
        "noImplicitAny": true,
        "sourceMap": true
    }
}

// File ../common/exclude.json
{
    "exclude": [
        "node_modules",
        "wwwroot"
    ]
}

// File ../common/tsconfig.util.json
{
    "files": [
        "foo.ts",
        "bar.ts"
    ]
}

Sie haben die Logik: files , compilerOptions , exclude ... kann auf andere tsconfig.json-Dateien verweisen und "nimmt" nur den passenden Schlüsselwortteil aus der anderen tsconfig .json-Datei => einfach und skalierbar. Sie können also eine tsconfig.json bei Bedarf in mehrere Dateien aufteilen und diese wiederverwenden.

Wenn Sie einen Teil Ihrer Diskussion lesen, scheint es am relevantesten, diese Sache mit dem "Sprachdienst" / der Goto-Definition richtig zu machen. JavaScript-Debugger verwenden sourceMaps. Wenn tsc jetzt sourceMap-Daten nicht nur in .js-, sondern auch in .d.ts-Dateien generiert...

Darüber hinaus sehe ich wirklich keinen großen Vorteil darin, Builds von untergeordneten Projekten aus der Datei tsconfig.json auszulösen. Wenn Sie diese grundlegende Art von Build-Zeitabhängigkeiten benötigen, würde ein einfaches Shell-Skript die Arbeit erledigen. Benötigt man hingegen intelligentes inkrementelles Bauen, erscheint der vorgeschlagene Ansatz zu einfach. In vielen Szenarien ist tsc nur ein Build-Schritt unter anderen. Wie seltsam, Abhängigkeiten für tsc in tsconfig.json zu schreiben, aber für den Rest eine andere Datei? Nochmals, für einfache Dinge, bei denen tsc der einzige Build-Schritt ist, den ein Shell-Skript ausführen würde.

Wie auch immer, wie wäre es mit dem Generieren von Quellzuordnungen in .d.ts-Dateien genauso wie in .js-Dateien?

wir verwenden einfach Node-Module + npm-Link, das einzige, was nicht funktioniert, ist, dass der moduleResolution: Node nicht mit ES6-Modulen kompatibel ist, die Inlining / Tree-Shaking-Optimierungen ermöglichen (siehe auch #11103 )

Um nicht vom Thema abzuweichen, aber in gewisser Weise scheint dies parallel zu den Herausforderungen der lokalen Arbeit mit mehreren Node-Paketprojekten zu sein. Ich glaube nicht, dass es so einfach ist, nur "npm link" zu verwenden. Sie haben möglicherweise unterschiedliche Build-Skripts, Sie müssen sie alle in der richtigen Reihenfolge ausführen, es ist schwieriger, dies inkrementell zu tun, es ist schwieriger, den Überwachungsmodus zu verwenden, es ist schwieriger, Dinge zu debuggen und es ist schwieriger, Quellzuordnungen aufzulösen. Je nach Redakteur Ihrer Wahl kann dies noch schwieriger sein.

Im Allgemeinen gebe ich einfach auf, packe alles in ein riesiges Projekt und teile es dann in separate Pakete auf, wenn sie sich stabilisiert haben, aber das hilft nur, weil die Herausforderung seltener gestellt wird. Ich finde die ganze Erfahrung wirklich sehr nervig. Verpasse ich etwas?

Was auch immer das sein mag, ich hoffe nur, dass ich endlich eine elegante Lösung für diese gesamte Entwicklungserfahrung haben kann.

Einfach mal einstimmen, das wäre toll für unsere tägliche Arbeit!

Aufgrund der Natur der Webentwicklung haben wir mehrere ts-Projekte, wobei jedes Projekt mehrere ts-Dateien enthält, die in einer einzigen js-Datei kompiliert sind ( --outFile ). Dies sind entweder App-ähnliche Projekte (sie tun eine bestimmte Sache oder fügen eine bestimmte Funktion hinzu) oder lib-ähnliche Projekte (wiederverwendbarer Code für die Apps). Oft arbeiten wir an mehreren dieser ts-Projekte gleichzeitig und verbessern die Bibliotheken, um die Entwicklung der Apps zu erleichtern. Aber ich glaube auch, dass keiner meiner Entwicklerkollegen alle unsere ts-Projekte zu jeder Zeit in seiner lokalen Umgebung hat.

Um unseren Workflow zu verbessern, sind unsere aktuellen Optionen

  • Werfen Sie alles in 1 ts Projekt

    • Wir können direkt auf .ts-Dateien verweisen, was viel schöner ist, als die d.ts-Dateien zu verwenden

    • Wir benötigen jedoch ein externes Tool, um Dateigruppen abzubilden und zu verketten, da wir die über die Interwebs angeforderten js-Dateien begrenzen müssen, während die Modularität der Apps (die funktions- oder seitenspezifisch sind) erhalten bleibt.

    • Wie bereits erwähnt, sind nicht alle diese ts-Projekte zu jeder Zeit für alle erforderlich, daher würde dies das Dateisystem stark aufblähen. Jemand, der an Projekt X arbeitet, benötigt möglicherweise die Projekte A, B und D, aber jemand, der an Y arbeitet, benötigt möglicherweise A und C.

  • Halten Sie Projekte getrennt (unsere aktuelle Situation)

    • Wir müssen auf kompilierte d.ts-Dateien aus den anderen ts-Projekten verweisen, da das direkte Einschließen einer .ts-Datei diese zur Ausgabe hinzufügen würde. Es wäre viel schneller, wenn wir einfach f12 direkt in unseren eigenen Quellcode einfügen könnten.

    • Und wir müssen Tools/Skripte hinzufügen, um mehrere dieser Projekte gleichzeitig zu kompilieren. Derzeit starten wir entweder tsc -d -w Befehle von mehreren Terminals aus oder starten ein Skript, das dies für alle Unterverzeichnisse tut, in denen eine tsconfig gefunden wird.

    • Ich verstehe, dass andere Tools dabei helfen können (zB gulp) oder vielleicht können wir etwas mit Dateiglobs in den tsconfigs herausfinden, aber das würde bei der ersten Ausgabe der d.ts-Dateien nicht helfen.

Unser Projekt(e) ist/sind einfach nicht groß genug, um die Entwicklung der Bibliotheken strikt von den Apps zu trennen, aber auch nicht klein genug, um einfach alles zusammenzuwerfen. Uns fehlen anmutige Optionen mit Typoskript.

Wenn dependencies die beste Option aus beiden Welten sein kann, wäre das erstaunlich; die Funktionalität von-Tags für alle ts-Dateien einer Abhängigkeit, aber kompilieren Sie die Ausgabe gemäß der eigenen tsconfig dieser Abhängigkeit.

Gibt es zu diesem Thema ein Update. Wie können wir mehrere Typoskript-Projekte haben, die unabhängig voneinander kompilieren? Wie können wir eine solche Abhängigkeit in tsconfig.json beschreiben?

Dies ist nun in der Roadmap im zukünftigen Abschnitt mit dem Titel „Unterstützung bei Projektreferenzen“ enthalten. Ich vermute also, dass eine .tsconfig Datei in der Lage sein wird, eine andere .tsconfig Datei als Abhängigkeit zu verknüpfen.

Gibt es zu diesem Thema ein Update. Wie können wir mehrere Typoskript-Projekte haben, die unabhängig voneinander kompilieren? Wie können wir eine solche Abhängigkeit in tsconfig.json beschreiben?

Die Build-Abhängigkeit sollte in Ihrem Build-System codiert sein. build-Systeme wie gulp, grunt, broccoli, msbuild, basal, etc. sind für solche Fälle gebaut.
Für Typinformationen sollte die Ausgabe eines Projekts eine .d.ts-Datei enthalten und diese als Eingabe an das andere übergeben werden.

@mhegazy Unser Projekt funktioniert so. Wir haben eine Reihe von Paketen in einem lerna monorepo, jedes Paket hat seine eigenen Abhängigkeiten in package.json und dieselben Abhängigkeiten in der Eigenschaft "types" in ihrem tsconfig.json . Jedes Projekt wird mit --outFile kompiliert (es ist ein älteres Projekt, das noch nicht in ES-Module verschoben wurde) und die "typings" package.json Schlüssel verweisen auf die gebündelten .d.ts Datei.

Wir verwenden gulp zum Bündeln/Beobachten.

Es funktioniert größtenteils, aber es gibt ein paar Probleme:

  • Aufgrund von Problemen wie Nr. 15488 und Nr. 15487 benötigen wir einen expliziten Link, damit Verweise korrekt funktionieren.
  • Go-to-Definition führt Sie zu einer gebündelten .d.ts Datei. Idealerweise führt dies Sie zur Quelle in einem anderen Projekt.
  • Der schnellste Weg, einen vollständigen Build durchzuführen, ist lerna run build --sort (tatsächlich tsc in jedem Verzeichnis), was zusätzlichen Aufwand mit sich bringt, da für jedes Paket ein TypeScript-Compiler-Prozess erzeugt wird, der viele wiederholte Arbeiten ausführt .

Ich behalte dieses Problem im Auge, da wir uns auch in der gleichen Situation befinden, die andere beschrieben haben.
Mehrere "Projekte", jedes mit seiner Datei tsconfig.json.

Wir haben den Build-Prozess, wie @mhegazy darauf hingewiesen hat: Jedes Projekt gibt eine .d.ts Datei aus, die als Eingabe für die abhängigen Projekte verwendet wird.

Das eigentliche Problem ist die IDE-Unterstützung: Bei der Suche nach Referenzen werden diese nur innerhalb eines einzigen tsconfig.json . Schlimmer noch, die kaskadierenden Effekte einer geänderten Datei werden nicht projektübergreifend weitergegeben, da abhängige Dateien außerhalb des tsconfig.json Bereichs nicht neu kompiliert werden. Dies ist sehr schlecht für die Wartung unserer Projekte und führt manchmal zu Build-Fehlern, die in der IDE abgefangen worden sein könnten.

It's happening

DU MEINE GÜTE

Ein aktualisiertes Szenario, in dem ich dies gerne hätte, beinhaltet React-Komponenten. Wir haben ein Komponenten-Repository, das JSX-Module (Atome, Moleküle und Organismen) enthält, die UI-Komponenten für alle Anwendungen in unserem Unternehmen geeignet machen. Dieses Komponenten-Repository wird von allen Front-End-Entwicklern verwendet, während sie an ihren individuellen Anwendungen arbeiten. Es wäre SO SCHÖN, wenn ich eine Erfahrung mit TypeScript-Sprachdiensten hätte, die es mir ermöglichen würde, die Benutzeroberfläche meiner spezifischen Anwendung zu bearbeiten und "Zur Definition" in das allgemeine UI-Komponenten-Repository zu wechseln. Heute müssen wir diese Komponenten einzeln bündeln und kopieren. Dies ist das Problem "Make your own project Sanitär", das ich gerne behoben sehen würde (für das es eine sehr schöne Geschichte in der .NET-Welt mit Projekten unter einer Lösung gibt).

Projektreferenzen: Integrierte Skalierbarkeit für TypeScript

Einführung

TypeScript wurde auf Projekte mit Hunderttausenden von Zeilen hochskaliert, unterstützt diese Art der Skalierung jedoch nicht nativ als integriertes Verhalten. Teams haben Workarounds mit unterschiedlicher Effektivität entwickelt und es gibt keine standardisierte Möglichkeit, große Projekte darzustellen. Obwohl wir die Leistung des Typecheckers im Laufe der Zeit stark verbessert haben, gibt es immer noch harte Grenzen, wie schnell TS vernünftig werden kann.
und Einschränkungen wie 32-Bit-Adressraum, die verhindern, dass der Sprachdienst in interaktiven Szenarien "unendlich" hochskaliert wird.

Intuitiv sollte das Ändern einer JSX-Zeile in einer Front-End-Komponente keine erneute Typprüfung der gesamten Kerngeschäftslogikkomponente eines 500.000 LOC-Projekts erfordern. Das Ziel von Projektreferenzen ist es, Entwicklern Tools zur Verfügung zu stellen , um ihren Code in kleinere Blöcke zu unterteilen. Indem wir Tools ermöglichen, mit kleineren Arbeitsstücken gleichzeitig zu arbeiten, können wir die Reaktionsfähigkeit verbessern und die Kernentwicklungsschleife straffen.

Wir glauben, dass unsere aktuelle Architektur nur sehr wenige verbleibende "kostenlose Mittagessen" in Bezug auf drastische Verbesserungen der Leistung oder des Speicherverbrauchs hat. Stattdessen ist diese Partitionierung ein expliziter Kompromiss , der die Geschwindigkeit auf Kosten einiger Vorarbeiten erhöht. Entwickler müssen einige Zeit damit verbringen, über den Abhängigkeitsgraphen ihres Systems nachzudenken, und bestimmte interaktive Funktionen (z. B. projektübergreifende Umbenennungen) sind möglicherweise nicht verfügbar, bis wir die Tools weiter verbessern.

Wir werden die wichtigsten Einschränkungen identifizieren, die von diesem System auferlegt werden, und Richtlinien für die Projektgröße, Verzeichnisstruktur und Build-Muster festlegen.

Szenarien

Es sind drei Hauptszenarien zu berücksichtigen.

Relative Module

Einige Projekte verwenden in großem Umfang relative Importe . Diese Importe werden eindeutig in eine andere Datei auf der Festplatte aufgelöst. Pfade wie ../../core/utils/otherMod werden häufig gefunden, obwohl in diesen Repos normalerweise flachere Verzeichnisstrukturen bevorzugt werden.

Beispiel

Hier ist ein Beispiel aus dem Perseus-Projekt der Khan Academy:

Angepasst von https://github.com/Khan/perseus/blob/master/src/components/graph.jsx

const Util = require("../util.js");
const GraphUtils = require("../util/graph-utils.js");
const {interactiveSizes} = require("../styles/constants.js");
const SvgImage = require("../components/svg-image.jsx");

Beobachtungen

Obwohl die Verzeichnisstruktur eine Projektstruktur impliziert , ist sie nicht unbedingt endgültig. Im obigen Beispiel der Khan Academy kann will ableiten, dass util , styles und components wahrscheinlich ihr eigenes Projekt sind. Es ist aber auch möglich, dass diese Verzeichnisse recht klein sind und tatsächlich in einer Build-Unit zusammengefasst werden.

Mono-Repo

Ein Mono-Repo besteht aus einer Reihe von Modulen, die über nicht relative Pfade importiert werden. Importe aus Untermodulen (zB import * as C from 'core/thing ) können üblich sein. Normalerweise, aber nicht immer, wird jedes Root-Modul tatsächlich auf NPM veröffentlicht.

Beispiel

Angepasst von https://github.com/angular/angular/blob/master/packages/forms/src/validators.ts

import {InjectionToken, ɵisObservable as isObservable, ɵisPromise as isPromise} from '@angular/core';
import {forkJoin} from 'rxjs/observable/forkJoin';
import {map} from 'rxjs/operator/map';
import {AbstractControl, FormControl} from './model';

Beobachtungen

Die Divisionseinheit ist nicht unbedingt der führende Teil des Modulnamens. rxjs zum Beispiel kompiliert tatsächlich seine Unterteile ( observable , operator ) wie jedes Scoped-Paket (zB @angular/core ).

Outfile

TypeScript kann seine Eingabedateien zu einer einzigen Ausgabe-JavaScript-Datei verketten. Referenzdirektiven oder Dateireihenfolge in tsconfig.json erstellen eine deterministische Ausgabereihenfolge für die resultierende Datei. Dies wird selten für neue Projekte verwendet, ist aber bei älteren Codebasen (einschließlich TypeScript selbst) immer noch weit verbreitet.

Beispiel

https://github.com/Microsoft/TypeScript/blob/master/src/compiler/tsc.ts

/// <reference path="program.ts"/>
/// <reference path="watch.ts"/>
/// <reference path="commandLineParser.ts"/>

https://github.com/Microsoft/TypeScript/blob/master/src/harness/unittests/customTransforms.ts

/// <reference path="..\..\compiler\emitter.ts" />
/// <reference path="..\harness.ts" />

Beobachtungen

Einige Lösungen, die diese Konfiguration verwenden, laden jedes outFile über ein separates script Tag (oder gleichwertig), andere (z. B. TypeScript selbst) erfordern jedoch eine Verkettung der vorherigen Dateien, da sie monolithische Ausgaben erstellen .

Projektreferenzen: Eine neue Isolationseinheit

Einige kritische Beobachtungen aus der Interaktion mit realen Projekten:

  • TypeScript ist normalerweise "schnell" (< 5-10s), wenn Projekte unter 50.000 LOC Implementierungscode (nicht-.d.ts) überprüft werden
  • .d.ts-Dateien, insbesondere unter skipLibCheck , sind in Bezug auf Typprüfung und Speicherkosten fast "kostenlos".
  • Nahezu jede Software lässt sich in Komponenten unterteilen, die kleiner als 50.000 LOC . sind
  • Fast alle großen Projekte erzwingen bereits eine gewisse Strukturierung ihrer Dateien nach Verzeichnissen, so dass Unterkomponenten mittlerer Größe entstehen
  • Die meisten Bearbeitungen erfolgen in Leaf-Node- oder Near-Leaf-Node-Komponenten, die keine erneute Überprüfung oder erneute Ausgabe der gesamten Lösung erfordern

Wenn es möglich wäre, immer nur einen 50.000 LOC-Teil des Implementierungscodes gleichzeitig zu überprüfen, würde es in einem interaktiven Szenario fast keine "langsamen" Interaktionen geben, und der Speicher würde fast nie ausgehen.

Wir führen ein neues Konzept ein, eine Projektreferenz , die eine neue Art von Abhängigkeit zwischen zwei TypeScript-Kompilierungseinheiten deklariert, bei denen der Implementierungscode der abhängigen Einheit nicht überprüft wird; stattdessen laden wir einfach seine .d.ts Ausgabe von einem deterministischen Ort.

Syntax

Eine neue Option references (TODO: Bikeshed!) wird zu tsconfig.json hinzugefügt:

{
  "extends": "../tsproject.json",
  "compilerOptions": {
    "outDir": "../bin",
    "references": [
      { "path": "../otherProject" }
    ]
  }
}

Das Array references gibt einen Satz anderer Projekte an, auf die von diesem Projekt aus verwiesen werden soll.
Jedes references Objekt path zeigt auf eine tsconfig.json Datei oder einen Ordner, der eine tsconfig.json Datei enthält.
Andere Optionen können zu diesem Objekt hinzugefügt werden, wenn wir ihre Bedürfnisse entdecken.

Semantik

Projektreferenzen ändern das folgende Verhalten:

  • Wenn die Modulauflösung in eine .ts Datei in einem Unterverzeichnis von rootDir eines Projekts aufgelöst würde, wird sie stattdessen in eine .d.ts Datei in outDir dieses Projekts aufgelöst

    • Wenn diese Auflösung fehlschlägt, können wir sie wahrscheinlich erkennen und einen intelligenteren Fehler ausgeben, z. B. Referenced project "../otherProject" is not built anstelle eines einfachen "Datei nicht gefunden"

  • Sonst nichts (TODO: bisher?)

Einschränkungen für die Leistung von referenzierten Projekten

Um die Build-Leistung deutlich zu verbessern, müssen wir das Verhalten von TypeScript einschränken, wenn es eine Projektreferenz sieht.

Im Einzelnen sollten folgende Dinge zutreffen:

  • Lesen oder parsen Sie niemals die .ts-Eingabedateien eines referenzierten Projekts
  • Nur die tsconfig.json eines referenzierten Projekts sollten von der Festplatte gelesen werden
  • Eine Aktualisierung auf dem neuesten Stand sollte nicht erfordern, dass die oben genannten Einschränkungen verletzt werden

Um diese Versprechen einzuhalten, müssen wir einige Einschränkungen für Projekte auferlegen, auf die Sie verweisen.

  • declaration wird automatisch auf true . Es ist ein Fehler, diese Einstellung zu überschreiben
  • rootDir standardmäßig "." (das Verzeichnis, das die tsconfig.json Datei enthält), anstatt aus den Eingabedateien abgeleitet zu werden
  • Wenn ein files Array bereitgestellt wird, muss es die Namen aller Eingabedateien bereitstellen

    • Ausnahme: Dateien, die als Teil von Typreferenzen enthalten sind (zB die in node_modules/@types ) müssen nicht angegeben werden

  • Jedes Projekt, auf das verwiesen wird, muss selbst ein references Array haben (das leer sein kann).

Warum "declaration": true ?

Projektreferenzen verbessern die Buildgeschwindigkeit, indem Deklarationsdateien (.d.ts) anstelle ihrer Implementierungsdateien (.ts) verwendet werden.
Daher muss für jedes referenzierte Projekt natürlich die Einstellung declaration aktiviert sein.
Dies wird durch "project": true impliziert

Warum rootDir ändern?

rootDir steuert, wie Eingabedateien Ausgabedateinamen zugeordnet werden. Das Standardverhalten von TypeScript besteht darin, das gemeinsame Quellverzeichnis des vollständigen Graphen der Eingabedateien zu berechnen. Zum Beispiel erzeugt die Menge der Eingabedateien ["src/a.ts", "src/b.ts"] die Ausgabedateien ["a.js", "b.js"] ,
aber die Menge der Eingabedateien ["src/a.ts", "b.ts"] erzeugt die Ausgabedateien ["src/a.js", "b.js"] .

Um den Satz von Eingabedateien zu berechnen, muss jede Root-Datei und alle ihre Referenzen rekursiv geparst werden.
was bei einem großen Projekt teuer ist. Aber wir können dieses Verhalten heute nicht ändern, ohne bestehende Projekte auf schlechte Weise zu zerstören, daher tritt diese Änderung nur auf, wenn das Array references bereitgestellt wird.

Keine Kreisförmigkeit

Projekte dürfen natürlich keine kreisförmige Grafik bilden. (TODO: Welche Probleme verursacht dies tatsächlich , abgesehen von Albträumen der Build-Orchestrierung?) In diesem Fall wird eine Fehlermeldung angezeigt, die den gebildeten Kreispfad angibt:

TS6187: Project references may not form a circular graph. Cycle detected:
    C:/github/project-references-demo/core/tsconfig.json ->
    C:/github/project-references-demo/zoo/tsconfig.json ->
    C:/github/project-references-demo/animals/tsconfig.json ->
    C:/github/project-references-demo/core/tsconfig.json

tsbuild

Dieser Vorschlag ist absichtlich vage hinsichtlich der Verwendung in einem "echten" Build-System. Nur sehr wenige Projekte skalieren über die "schnelle" Grenze von 50.000 LOC hinaus, ohne etwas anderes als tsc für die Kompilierung von .ts-Code einzuführen.

Das Benutzerszenario von "Sie können foo nicht erstellen, weil bar noch nicht erstellt wurde" ist eine offensichtliche "Los und hol das"-Aufgabe, um die sich ein Computer kümmern sollte als eine mentale Belastung für Entwickler.

Wir erwarten, dass Tools wie gulp , webpack usw. (oder ihre jeweiligen TS-Plugins) das Verständnis von Projektreferenzen aufbauen und diese Build-Abhängigkeiten korrekt behandeln, einschließlich der aktuellen Überprüfung.

Um sicherzustellen, dass dies möglich ist , stellen wir eine Referenzimplementierung für ein TypeScript-Buildorchestrierungstool bereit, das die folgenden Verhaltensweisen demonstriert:

  • Schnelle Aktualitätsprüfung
  • Bestellung von Projektdiagrammen
  • Parallelisierung erstellen
  • (TODO: andere?)

Dieses Tool sollte nur öffentliche APIs verwenden und gut dokumentiert sein, damit die Autoren von Build-Tools verstehen, wie Projektreferenzen korrekt implementiert werden.

MACHEN

Abschnitte, die ausgefüllt werden müssen, um dieses Angebot vollständig zu vervollständigen

  • So übertragen Sie ein bestehendes Projekt

    • Legen Sie im Grunde einfach tsconfig.json Dateien ab und fügen Sie dann Referenzen hinzu, die zum Beheben von Build-Fehlern erforderlich sind

  • Auswirkungen auf baseUrl

    • Erschwert die Implementierung, aber effektiv keine Auswirkungen auf den Endbenutzer

  • Kurze Diskussion von Verschachtelungsprojekten (TL;DR muss erlaubt sein)
  • Skizzieren Sie das Szenario, ein Projekt, das "zu groß" geworden ist, in kleinere Projekte zu unterteilen, ohne die Verbraucher zu verletzen
  • Finde das Lerna-Szenario heraus

    • Der verfügbare Datenpunkt (N = 1) sagt, dass sie dies nicht benötigen würden, da ihr Build bereits auf diese Weise effektiv strukturiert ist

    • Finden Sie weitere Beispiele oder Gegenbeispiele, um besser zu verstehen, wie Menschen dies tun

  • Brauchen wir eine dtsEmitOnly Einstellung für Leute, die ihr JS zB durch webpack/babel/rollup leiten?

    • Vielleicht impliziert references + noEmit dies

Fantastisch!

Finde das Lerna-Szenario heraus

  • Der verfügbare Datenpunkt (N = 1) sagt, dass sie dies nicht benötigen würden, da ihr Build bereits auf diese Weise effektiv strukturiert ist

Bezieht sich "dies" auf den Vorschlag oder die Referenz-Build-Implementierung? Während Sie lerna verwenden könnten, um den Build durchzuführen (mein Team tut), ist es knorrig und viel effizienter, wenn TS (oder ein aus diesem Vorschlag erstelltes Tool) sich von selbst erledigt.

Der TODO-Bereich ist TODOs für den gesamten Vorschlag

Schön!

Jedes referenzierte Projekt muss selbst ein Referenzarray haben (das leer sein kann).

Ist das wirklich notwendig? Wäre es nicht ausreichend, wenn ein solches Paket .d.ts Dateien enthält?
(In diesem Fall ist es möglicherweise nicht einmal notwendig, dass es auch ein tsconfig.json gibt?)

Mein Anwendungsfall: Betrachten Sie ein Projekt (z. B. von Drittanbietern), das nicht outDir , also werden .ts , .js und .d.ts daneben stehen einander, und TS wird derzeit versuchen, .ts zu kompilieren, anstatt .d.ts .

Der Grund, outDir zu verwenden, ist für mich, import "package/subthing" -Style-Importe einfacher zuzulassen, die sonst zB import "package/dist/subthing" mit outDir: "dist" müssten.
Und um entweder das NPM-Paket oder sein Quell-Repository direkt verwenden zu können (zB mit npm link ).

(Es wäre hilfreich, wenn package.json die Angabe eines Verzeichnisses in main erlaubt, aber leider ...)

Brauchen wir eine dtsEmitOnly-Einstellung für Leute, die ihr JS durch zB webpack/babel/rollup leiten?

Absolut! Dies ist im Moment ein großes fehlendes Stück. Derzeit können Sie eine einzelne d.ts-Datei erhalten, wenn Sie outFile , aber wenn Sie zu Modulen wechseln und einen Bundler verwenden, verlieren Sie diese. Es wäre erstaunlich, eine einzelne d.ts-Datei für den Einstiegspunkt eines Moduls (mit export as namespace MyLib ) ausgeben zu können. Ich weiß, dass externe Tools dies können, aber es wäre wirklich großartig, wenn sie in die Emitter- und Sprachdienste integriert würden.

Ist das wirklich notwendig? Wäre es nicht ausreichend, wenn ein solches Paket .d.ts-Dateien enthält?

Wir brauchen etwas in der Ziel-tsconfig, das uns sagt, wo die Ausgabedateien zu erwarten sind. Eine frühere Version dieses Vorschlags hatte "Sie müssen ein explizites rootDir angeben ", was ziemlich umständlich war (Sie mussten "rootDir": "." in jede tsconfig schreiben). Da wir eine Vielzahl von Verhaltensweisen in dieser Welt umkehren möchten, war es sinnvoller zu sagen, dass Sie ein "Projekt" -Verhalten erhalten, wenn Sie ein Referenzarray haben und dies das ist, was abgetastet wird, anstatt eine Reihe von anzugeben Flags, die Sie explizit angeben müssten.

Dieser Vorschlag würde eng mit der Struktur unserer TypeScript-Projekte übereinstimmen. Wir haben in kleinere Einheiten unterteilt, die jeweils eine tsconfig.json haben und unabhängig durch gulp gebaut werden. Projekte verweisen aufeinander, indem sie auf die d.ts-Dateien verweisen.

In einer idealen Welt müsste das referenzierte Projekt nicht vorgefertigt werden. dh. TypeScript führt einen "Build" des referenzierten Projekts durch und behält das "d.ts"-Äquivalent im Speicher des Sprachdienstes bei. Dies würde es ermöglichen, dass im "Quell"-Projekt vorgenommene Änderungen im "abhängigen" Projekt angezeigt werden, ohne dass ein Neuaufbau erforderlich ist.

Wir brauchen etwas in der Ziel-tsconfig, das uns sagt, wo die Ausgabedateien zu erwarten sind.

Das ist nur wahr, wenn outDir verwendet wird, nicht wahr?

Wie in: wenn ich eine tsconfig habe, die:

  • verwendet NICHT outDir (aber natürlich declaration: true ), dann brauchen wir weder rootDir noch references
  • hat outDir , dann müssten Sie references und/oder rootDir (und declaration: true ) setzen

Grund für die Frage ist, dass ich dann den 'Projektmodus' für jedes TS-Paket aktivieren könnte, indem ich einfach darauf referenziere, dh es liegt in meiner Kontrolle.

In diesem Fall wäre es auch gut, wenn es auch funktioniert, sobald es die gesuchte .d.ts-Datei findet (dh sich nicht beschweren, wenn keine .ts-Dateien oder tsconfig-Dateien vorhanden sind). Denn dies ermöglicht einen weiteren Fall des "Ersetzens" einer NPM-Version (die möglicherweise nur .d.ts-Dateien enthält) bei Bedarf durch ihre Quellversion.

Betrachten Sie beispielsweise die NPM-Pakete MyApp und SomeLib.
SomeLib könnte tsconfig haben: declaration: true .

Repository wie:

package.json
tsconfig.json
index.ts
sub.ts

Zusammengestellt wird daraus:

package.json
tsconfig.json
index.ts
index.d.ts
index.js
sub.ts
sub.d.ts
sub.js

Diese Struktur ermöglicht zB

// somewhere in MyApp
import something from "SomeLib/sub";

Im veröffentlichten NPM-Paket muss ich derzeit immer die .ts-Dateien entfernen, sonst werden alle Quellen von TS neu kompiliert, wenn MyApp SomeLib verwendet:

Auf NPM wird dies also zu:

package.json
index.d.ts
index.js
sub.d.ts
sub.js

Wenn ich jetzt references: ["SomeLib"] in die tsconfig von MyApp eintrage, wäre es gut, wenn es sowohl für die NPM-Version als auch für die Quellversion von SomeLib 'wie ist' funktioniert , dh dass es sich nicht über fehlende tsconfig beschwert, solange es sub.d.ts an der richtigen Stelle findet.


Verwandte, aber andere Frage:

Mir ist jetzt klar, dass, wenn der Autor von SomeLib references in seine tsconfig einfügt, dies in Zukunft ermöglichen würde, NPM-Pakete MIT den .ts-Dateien zu veröffentlichen. Aber ich nehme an, TS würde diese immer noch immer neu kompilieren, wenn ein abhängiges Paket nicht explizit references: ["SomeLib"] in seine tsconfig schreibt.

Oder ist die Absicht auch, dass references in MyLib automatisch auch eine 'Projektgrenze' einführt, wenn nur import 'ing' (also nicht references 'ing)?

IIRC, eine der anfänglichen Ideen war, dass, wenn ein Modul z. B. über node_modules lokalisiert wurde, dann .d.ts Dateien gegenüber .ts Dateien bevorzugt würden, aber dies wurde später wieder geändert , weil die Heuristik ("through node_modules ") im Allgemeinen zu problematisch war. Könnte es sein, dass eine explizite 'Projektgrenze' dies lösen würde (zB ein projectRoot: true , anstatt oder zusätzlich zu references )?

Für den Fall lerna hatte ich auf eine einfachere Lösung gehofft.

  1. In den einzelnen Paketverzeichnisse, wissen die Pakete sollten nicht von irgendetwas in Bezug auf die monorepo Struktur. Die einzelnen TSconfig json Dateien sollten keine Verweise enthalten.
  2. Im Root-"Workspace"-Repository (das den gesamten Code enthalten kann, aber auch einfach andere Repositorys klonen kann), haben Sie die Referenzen in seiner tsconfig.json

    • Dies geschieht nur, damit die Tools Verweise bis zum Quellcode erkennen und verfolgen können, anstatt in .d.ts-Dateien zu gehen.

Meine ganze Sorge ist, dass, sobald Sie eine Referenz mit einem relativ absteigenden "../xx" Pfad zu den einzelnen Projektkonfigurationsdateien hinzufügen, diese nicht mehr als eigenständige Module verwendet werden können - sie müssen sich in einer bestimmten Arbeitsbereichsstruktur befinden.

Das Hinzufügen des neuen Konzepts eines "Arbeitsbereichs" tsconfig.json löst dieses Problem. Auf diese Weise sollten Sie, wenn Sie zB das einzelne Paket "git clonen" und seine Abhängigkeiten auf normale Weise installieren (zB mit npm oder garar), separat daran arbeiten, da die kompilierten Abhängigkeiten ihre Definitionsdateien mitbringen würden. Wenn Sie den gesamten Arbeitsbereich klonen und den Befehl ausführen, um alle Pakete einzufügen, können Sie mit der Arbeitsbereichskonfiguration durch alle Quellen navigieren.

Beachten Sie, dass ein Arbeitsbereich tsconfig.json auch perfekt zu Yarns Arbeitsbereich package.json passt https://yarnpkg.com/lang/en/docs/workspaces/

Ich habe hier einen kleinen Proof of Concept gemacht

https://github.com/spion/typescript-workspace-plugin

Fügen Sie das Plugin einfach zu all Ihren tsconfig.json Dateien der einzelnen Repos hinzu

{
  "plugins": [{"name": "typescript-workspace-plugin"}]
}

Fügen Sie dann auf der obersten Ebene package.json neben dem Eintrag "workspaces" von Garn einen Eintrag "workspace-sources" hinzu:

{
  "workspaces": ["packages/*"],
  "workspace-sources": {
    "*": ["packages/*/src"]
  }
}

Das Feld funktioniert genau wie das Feld "paths" in tsconfig.json, betrifft aber nur den Sprachdienst der einzelnen Projekte und verweist auf die Paketquellen. Stellt die richtige "Gehe zu Definition / Typ"-Funktionalität und ähnliches wieder her.

Das gilt nur, wenn outDir verwendet wird, nicht wahr?

Richtig. Wir hatten angenommen, dass fast jeder mit einem großen Projekt outDir . Es würde mich interessieren, von Projekten zu hören, die dies nicht tun

Wenn ich nun Referenzen: ["SomeLib"] in die tsconfig von MyApp einfüge, wäre es gut, wenn es sowohl für die NPM-Version als auch für die Quellversion von SomeLib 'wie ist' funktioniert

Großer Fan, diese Idee gefällt mir sehr. Ich muss darüber nachdenken, ob es wirklich erforderlich ist oder nicht.

Eine Einschränkung hier ist, dass Paketautoren meiner Meinung nach entweder a) sowohl .ts- als auch tsconfig-Dateien zusammen an einem Ort veröffentlichen müssen, an dem TS sie findet, oder b) keines veröffentlichen und nur .d.ts-Dateien erreichbar haben. Im (a) Fall würden wir den Projektreferenzen rekursiv folgen und das Richtige würde passieren und im (b) würden wir nicht an die falschen Stellen spinnen.

Oder ist auch beabsichtigt, dass Referenzen in MyLib automatisch auch eine 'Projektgrenze' einführen, wenn sie nur importiert werden (dh nicht referenziert werden)?

Wir haben mit @mhegazy gesprochen und wir denken, dass es tatsächlich ein einfaches Modell für das Verhalten von Projektreferenzen gibt: references Array "sieht" niemals eine .ts Datei außerhalb des Projektordners - dies schließt Dateien ein unter exclude d Verzeichnissen. Allein durch diese Änderung funktioniert das Lerna-Szenario ("work", was "Modulreferenzen werden immer in .d.ts auflösen") sofort funktioniert, ebenso wie andere.

Ich muss mir das "Workspace" -Modell mehr ansehen.

Das gilt nur, wenn outDir verwendet wird, nicht wahr?

Richtig. Wir hatten angenommen, dass fast jeder mit einem großen Projekt outDir verwendet. Es würde mich interessieren, von Projekten zu hören, die dies nicht tun

Wir haben 67 TS-Projekte in der Visual Studio-Lösung, die ohne outdir und Postbuild-Grunttasks kompiliert werden, um die Ausgabeverzeichnisstruktur (und Uglify und andere Nachbearbeitungen) zu erstellen.

Die meisten Projekte haben eine solche tsconfig.json

 "include": [
    "../baseProj/Lib/jquery.d.ts",
    "../baseProj/baseProj.d.ts"
  ]

Ich habe mir einige Zeit genommen, um den Referenzvorschlag durchzulesen, und korrigiere - Benutzer von AFAICT lerna- und Garn-Workspaces benötigen keine der hier vorgeschlagenen Workspace-Funktionen:

  1. Wir haben bereits ein Abhängigkeitsdiagramm basierend auf package.json, sodass wir die Reihenfolge kennen, in der der Build ausgeführt werden soll. Tatsächlich verfügt lerna über einen generischen run-Befehl, der dies der Reihe nach ausführen kann, und es ist nicht schwierig, ein Tool zu schreiben, das gegebenenfalls auch Parallelität hinzufügt. skipLibCheck sollte die Auswirkungen auf die Leistung vernachlässigbar machen, aber ich habe es nicht überprüft.
  2. Lerna und Yarn erstellen bereits symbolische Links zu den anderen Modulen an der entsprechenden Position von node_modules. Infolgedessen können alle abhängigen Elemente der package.json des anderen Moduls folgen, das Feld "types/typings" lesen und die referenzierte Typdefinitionsdatei module.d.ts finden.

Was wir nicht haben und was das von mir geschriebene Plugin bietet, ist eine Möglichkeit, alle Quellen gleichzeitig zu laden. Wenn ich gleichzeitig Änderungen an zwei oder mehr Modulen vornehmen muss, möchte ich nicht, dass "Gehe zur Definition" und "Gehe zur Typdefinition" mich zur .d.ts-Datei schickt. Ich möchte, dass es mich an den Ort des ursprünglichen Quellcodes schickt, damit ich es vielleicht bearbeiten kann. Andernfalls würde ich nur das einzelne Projektverzeichnis laden und die von lerna/yarn erstellten symbolischen Verknüpfungen node_modules funktionieren einfach.

Bei uns das Gleiche. Anstelle von Lerna verwenden wir Rush , um unseren Abhängigkeitsgraphen zu berechnen, aber der Effekt ist der gleiche. Wenn wir Projekte erstellen, ist tsc nur eine von vielen Aufgaben, die ausgeführt werden müssen. Unsere Compileroptionen werden von einem größeren Build-System berechnet, und wir bewegen uns zu einem Modell, bei dem tsconfig.json keine Eingabedatei ist, sondern eine generierte Ausgabe (hauptsächlich zum Vorteil von VS Code).

Was wir nicht haben und was das von mir geschriebene Plugin bietet, ist eine Möglichkeit, alle Quellen gleichzeitig zu laden. Wenn ich gleichzeitig Änderungen an zwei oder mehr Modulen vornehmen muss, möchte ich nicht, dass "Gehe zur Definition" und "Gehe zur Typdefinition" mich zur .d.ts-Datei schickt. Ich möchte, dass es mich an den Ort des ursprünglichen Quellcodes schickt, damit ich es vielleicht bearbeiten kann.

+1 das wäre super.

Wenn wir von einer besseren Unterstützung für mehrere Projekte träumen, wäre meine erste Anfrage ein Compilerdienst, etwa wie VS Code IntelliSense funktioniert. Unsere Builds wären deutlich schneller, wenn Rush tsc 100-mal aufrufen könnte, ohne die Compiler-Engine 100-mal hochfahren zu müssen. Der Compiler ist einer unserer teuersten Build-Schritte. In einem Monorepo sind die Build-Zeiten wirklich wichtig.

@iclanton @nickpape-msft @qz2017

Ja bitte!

Ich denke, eines der nützlichsten Ergebnisse des Projektsystems wäre, wenn
'go to definition' ging zur Quelldatei anstelle der d.ts-Datei und
'Alle Referenzen finden' durchsucht den Projektreferenzbaum.

Vermutlich würde dies auch Refactorings vom Typ 'global rename' freischalten.

Am Donnerstag, 9. November 2017 um 21:30 Uhr Salvatore Previti [email protected]
schrieb:

Ja bitte!


Sie erhalten dies, weil Sie diesen Thread abonniert haben.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/Microsoft/TypeScript/issues/3469#issuecomment-343356868 ,
oder den Thread stumm schalten
https://github.com/notifications/unsubscribe-auth/AANX6d19Zz7TCd_GsP7Kzb-9XJAisG6Hks5s07VXgaJpZM4E-oPT
.

Wir haben mit @mhegazy gesprochen und wir glauben, dass es tatsächlich ein einfaches Modell für das Verhalten von Projektreferenzen gibt:

Schön, und warum müsste man dann überhaupt konkrete Referenzen angeben?
Es scheint, dass es ausreichen würde, ein Flag zu haben (wie projectRoot: true ).
Was wäre zum Beispiel der Unterschied zwischen references: ["foo"] und nur references: [] ?
Denn wenn ich import "foo" oder import "bar" , ignorieren beide alle .ts Dateien.

In diesem Fall lautet der Vorschlag also:

Angesichts dieser tsconfig.json (TODO bikeshed auf projectRoot ):

{
  "extends": "../tsproject.json",
  "compilerOptions": {
    "projectRoot": true
   }
}

Wenn tsc dann etwas außerhalb des Projektordners auflösen muss (einschließlich Dateien in ausgeschlossenen Verzeichnissen), werden nur .d.ts Dateien betrachtet (alternativ kann es nur _bevorzugen_ .d.ts Dateien und greifen Sie auf tsconfig und/oder .ts wenn es nur das sieht).

Dies macht die Auflösung während der Kompilierung schnell und einfach.
Es funktioniert für Monorepo-Referenzen (zB import "../foo" ) und paketbasierte Referenzen (zB import "foo" ).
Es funktioniert für NPM-Pakete und deren Quellcodedarstellung.
Und es macht das Auflösen von tsconfig.json Maschinen während des Kompilierens überflüssig, obwohl die Fehlermeldung weniger hilfreich ist, wenn es die .d.ts nicht finden kann.

Es klingt ein bisschen zu schön, um wahr zu sein, wenn es tatsächlich so einfach ist, also übersehe ich wahrscheinlich etwas Wichtiges :)


Wie andere auch betonen, ist es immer noch sehr wichtig, dass 'IntelliSense' weiterhin mit den .ts-Dateien arbeitet.

Wenn also eine Anfrage nach 'go to definition', 'find reference' usw. gestellt wird, sollte es eine umgekehrte Zuordnung verwenden, um die entsprechende .ts Datei aus der .d.ts Datei zu finden, die es so verwendet hat weit.

Diese Zuordnung könnte z. B. erfolgen mit:

  • Verwenden einer eingebetteten Kommentarzeichenfolge wie //# sourceURL = ../src/foo.ts im .d.ts

    • eine aufwändigere Sourcemap könnte verwendet werden, um von einem 'aufgerollten' .d.ts zurück zum ursprünglichen .ts zuzuordnen

  • Auflösen der .js Datei und Verwenden ihrer Quellzuordnung, um die `.ts . zu finden

Dies führt in das Thema der Neuerstellung der .d.ts wenn diese .ts geändert wird, aber ich bin mir nicht sicher, ob dies durch diesen Vorschlag gelöst werden sollte. Zum Beispiel ist es bereits heute der Fall, dass ein Prozess erforderlich ist, um die .js Dateien neu zu erstellen, sogar aus dem Root-Projekt selbst heraus. Ich gehe also davon aus, dass, wenn dies vorhanden ist, auch etwas zum Wiederherstellen der Abhängigkeiten vorhanden sein wird. Könnte ein Haufen paralleler tsc 's für jedes Paket sein, könnte tsbuild , könnte eine intelligente IDE mit compileOnSave ähnlichem Verhalten sein usw.

@pgonzal @michaelaird https://github.com/spion/typescript-workspace-plugin macht nur das - es stellt die Definition wieder her und findet alle Referenzen für ein multi-tsconfig-Garn/lerna/etc-Workspace-Projekt.

Interessant... Ich frage mich, ob wir das mit Rush zum Laufen bringen könnten. Wir werden einen Blick darauf werfen.

Zu Ihrer Information, ein weiteres einfaches Tool, das wir im Rahmen dieser Bemühungen entwickelt haben, war wsrun

Ähnlich wie lerna run führt es einen Befehl für alle Pakete im Arbeitsbereich aus.

Da Typoskript-Abhängigkeiten der Reihe nach kompiliert werden müssen, kann wsrun einen Paketbefehl in einer topologischen Reihenfolge basierend auf ihren package.json Abhängigkeiten ausführen. Es unterstützt Parallelität während des Builds. Es ist nicht optimal parallel, aber das kann später verbessert werden.

Nur eine Anmerkung, oao ist ein weiteres Monorepo-Tool für Garn. Kürzlich wurde auch die Unterstützung für die "topologische" Anordnung von Befehlen hinzugefügt.

Ich möchte hier nur das Wort "Refactoring" posten, da es für uns ein wichtiges Ziel ist, wenn auch wahrscheinlich tangential zum aktuellen Vorschlag (https://github.com/Microsoft/TypeScript/issues/3469#issuecomment-341317069) und wird in dieser Ausgabe nicht oft erwähnt.

Unser Anwendungsfall ist ein Monorepo mit mehreren TS-Projekten, es kann im Grunde auf ein common-library plus ein paar Apps reduziert werden: app1 und app2 . Wenn Sie in der IDE arbeiten, sollten diese als ein einzelnes Projekt behandelt werden, zB sollte das Umbenennen von Refactoring über alle drei Module hinweg funktionieren, aber app1 und app2 sind auch zwei separate Build-Ziele. Obwohl ich zustimme, dass Build im Allgemeinen ein separates Anliegen ist, ist die Wahrheit, dass unsere Apps ziemlich klein sind und etwas wie cd app1 && tsc für uns völlig in Ordnung wäre.

Wenn TypeScript eine gute Möglichkeit bietet, dies zu unterstützen, wäre das großartig.

Für das projektübergreifende Refactoring / Referenzen von Monorepo habe ich festgestellt, dass dieses Setup für mich funktioniert, wenn Sie in vscode arbeiten:

root-tsconfig:

"compilerOptions": {
   "baseUrl": ".",
   // global types are different per project
   "types": [],
   "paths": {
      "lib": ["packages/lib/src"],
      "xyz1": ["packages/xyz1/src"],
      "xyz2": ["packages/xyz2/src"],
   }
},
"include": ["./stub.ts"], // empty file with export {} to stop vscode complaining about no input files
"exclude": ["node_modules"]

Pakete/xyz1/tsconfig.json

{
  "extends": "../../tsconfig",
   "compilerOptions": {
      "types": ["node"],
   },
   "include": ["src/**/*"]
}

Pakete/xyz2/tsconfig.json

{
  "extends": "../../tsconfig",
   "compilerOptions": {
      "types": ["webpack-env"]
   },
  "include": ["src/**/*"]
} 

Pakete/lib/tsconfig.json

{
   "extends": "../../tsconfig",
    "compilerOptions": { ... },
    "include": ["src/**/*"],
    // special file to load referenced projects when inside in lib package, without it they won't be 
    // visible until you open some file in these projects 
    "files": ["./references.ts"],
}

Pakete/lib/tsconfig-build.json

{
   "extends": "./tsconfig",
    // exclude referenced projects when building
   "files": []
}

Pakete/lib/references.ts

import "xyz1";
import "xyz2";

export {};

Sie benötigen die korrekte main Eigenschaft in package.json Pakets und können types auch für No-Lib-Pakete sein, zum Beispiel:

  "main": "src/main.tsx",
  "types": "src/main.tsx",

Auf diese Weise werden beim Refactoring/Umbenennen von etwas in lib auch Referenzen in xyz1 und xyz2 umgestaltet. Auch Projekte könnten auf diese Weise unterschiedliche Globals/Target-Libs haben.

Bei Gradle nennen sie es einfach - Composite Build .

Übrigens, kann mir jemand sagen, wo ich anfangen soll, wenn ich zum TypeScript-Compiler beitragen möchte? Ich habe das Repo geklont und es ist keine kleine Sache (ich lese die Quelle: eckig, ionisch, Express, wenn ich es nicht aus den Dokumenten bekomme oder vom Internet weg bin ...) Ich brauche wirklich Hilfe von einem um mir den Weg zu zeigen, bitte.

Vielen Dank!
Eine glänzende Zukunft für TypeScript.

Ich habe einen Prototyp, den die Leute ausprobieren sollen, wenn sie relative Modulpfade verwenden. Unter https://github.com/RyanCavanaugh/project-references-demo gibt es ein Beispiel-Repo, das das grundlegende Szenario skizziert und zeigt, wie es funktioniert.

Um es vor Ort zu versuchen:

git clone https://github.com/RyanCavanaugh/TypeScript
git checkout pr-lkg
npm install
npm run build
npm link

Dann in deinem anderen Ordner:

npm link typescript

Ich werde das pr-lkg Tag beibehalten, das auf den neuesten funktionierenden Commit zeigt, wenn sich die Dinge ändern. Als nächstes zu meinen Todos:

  • [x] Bessere Fehlermeldungen hinzufügen, wenn abhängige Projekte nicht erstellt werden
  • [ ] Wenden Sie das Umleitungsverhalten auf nicht relative Modulpfade an
  • [ ] Stellen Sie eine API für Build-Tools zur Verfügung

@RyanCavanaugh Ich kann es aufgrund von Fehlern wie fehlendem del Modul oder Error: Cannot find module 'C:\github\TypeScript\built\local\tsc.js' (vielleicht Ihr lokaler Pfad?) nicht wirklich erstellen, aber insgesamt sieht die Struktur gut aus.

Ist dies nur tsc oder werden auch projektweite Refactorings in VSCode mit dem Sprachserver berücksichtigt?

...auch pr-lkg Tag fehlt.

Das Tag ist da (es ist kein Zweig). Siehe https://github.com/RyanCavanaugh/TypeScript/tree/pr-lkg

Das references in tsconfig.json ist ein Opt-in pro Abhängigkeit, wäre es nicht sinnvoller, es auf alles anzuwenden, was außerhalb von rootDir ?

Ich stelle mir so etwas wie eine sandbox Eigenschaft vor, die so aussehen könnte:

# tsconfig.json
{
  "compilerOptions": {
    "outDir": "lib",
    "sandbox": "."
  },
  "include": ["src/index.ts"]
}

Sandbox würde auch rootDir auf denselben Wert setzen. Anstatt explizit Pfade zu Verzeichnissen anzugeben, die tsconfig.json , würde die normale Modulauflösung gelten, und Sie könnten den FS-Baum durchsuchen, um die tsconfig.json automatisch zu finden.

# package.json
{
  "name": "animals",
  "module": "src",
  "typings": "lib",
  "dependencies": {
    "core": "*"
  }
}

Diesen Weg:

  • Sie müssen nicht zwei synchronisierte Listen pflegen ( references und dependencies ).
  • Package hat keine Kenntnis des Dateisystems außerhalb seines eigenen.
  • Verwenden Sie eine Strategie zur Auflösung von Knotenmodulen anstelle von benutzerdefinierten Pfaden.

@RyanCavanaugh , soweit ich das verstanden habe, kann ich mit diesen Änderungen nur lokal arbeiten und ich werde solche Projekte nicht zum Testen senden können, zum Beispiel auf travis-ci.org. Rechts?

Notizen von einem heutigen Meeting mit @billti / @mhegazy


  • Find References / Rename muss funktionieren, zumindest für Szenarien, in denen der notwendige Kompilierungskontext zum Bestimmen des Abschlusses von Projekten in den Speicher passt
  • Die lösungsweite Umbenennung wird durchlaufen, um eine "Lösungsdatei" zu finden, rückwärts zu arbeiten, um referenzierende Projekte zu finden, und dann die Abschnitte des Diagramms, aus denen das Symbol sichtbar ist, implementierungsladen . Dies kann ein iterativer Prozess sein, um die wahre Ursprungserklärung zu finden
  • tsbuild muss einen -w-Modus verarbeiten
  • Lösungen dürfen keine Unterprojekte haben, die ihren Ursprung außerhalb des Lösungsordners haben
  • Benötigen Sie eine klare Dokumentation für zB gulp, webpack

Sidebar: Diese Umbenennung funktioniert heute nicht

function f() {
  if (Math.random() > 0.5) {
    return { foo: 10 };
  } else {
    return { foo: 20 };
}
// rename foo here doesn't rename *both* instances in the function body
f().foo;

Danke Ryan, Mohamed und Bill. Dass das Szenario „Referenzen suchen/umbenennen“ projektübergreifend funktioniert, war einer der Hauptanwendungsfälle, die ich im Sinn hatte, als ich die ursprüngliche Beschwerde darüber vorbrachte, dass TypeScript keine mittelgroßen Projekte unterstützt. Mittelgroße Projekte sind modular, aber nicht groß. Der Vorschlag und die Arbeit, die ich hier bisher gesehen habe, fühlt sich eher wie ein Skalierbarkeitsspiel an. Dies ist sehr wichtig für die langfristige Gesundheit von TypeScript, aber es ist hauptsächlich für große Projekte von Vorteil, nicht für mittlere. Die Dinge, die ich in diesem Kommentar von Ryan höre, klingen eher nach dem, was erforderlich ist, um die Ergonomie bei der Entwicklung mittelgroßer Projekte in TypeScript zu verbessern.

Wie immer vielen Dank an das gesamte TypeScript-Team für Ihre Bemühungen! Du machst tolle Arbeit.

Es gibt einen Trick mit lerna/yarn workspace, der Ihnen das Leben viel einfacher macht.
Zeigen Sie die Einträge main und types in Ihrer package.json der Unterprojekte auf Ihren src/index. ts- Datei und das Szenario "Referenzen suchen/Umbenennen" funktionieren einfach.
Und Sie können Ihr gesamtes Projekt mit einem einzigen tsc kompilieren.
Sie können dies für einige Ihrer Pakete tun, Sie können dies für alle tun. Ihr Anruf.

Es gibt einige Nachteile und Fallstricke (Wenn Sie eine Erweiterung in einem Paket haben oder globale Symbole importieren, wird Ihr gesamtes Programm verunreinigt), aber im Allgemeinen funktioniert es sehr gut.
Wenn Sie in NPM veröffentlichen möchten, setzen Sie einfach die Haupt- und Typen auf die entsprechenden Werte (als Teil Ihres Builds oder so).

Mit dem obigen Setup habe ich mehr oder weniger alle erwarteten Funktionen erhalten

Hier ist ein Trick mit lerna/yarn workspace, der Ihnen das Leben viel einfacher macht.
Zeigen Sie die Haupt- und Typeneinträge in Ihrer package.json der Unterprojekte auf Ihre src/index.ts-Datei, und das Szenario Find References/Rename funktioniert einfach.

Meiner Erfahrung nach besteht das Problem bei diesem Setup darin, dass TypeScript die ts-Dateien von _externen_ Paketen so behandelt, als wären sie _Quellen_ des Pakets, das sie benötigt, und nicht als externe Bibliotheken. Dies verursacht eine Reihe von Problemen.

  • Externe Pakete werden mehrmals kompiliert, wobei jedes Mal die tsconfig des _requireing_-Pakets verwendet wird. Wenn die benötigten Pakete unterschiedliche tsconfigs (zB unterschiedliche Libs) haben, kann dies dazu führen, dass falsche Kompilierungsfehler auf dem benötigten Paket erscheinen, bis es erneut kompiliert wird.

  • Benötigte Pakete werden auch langsamer kompiliert, da sie mehr Dateien als nötig enthalten.

  • Das rootDir aller Pakete wird zum Verzeichnis der obersten Ebene und ermöglicht möglicherweise das direkte Einschließen jeder TS-Datei aus jedem Paket, anstatt nur index . Wenn Entwickler nicht aufpassen, können sie die erforderliche Paket-API umgehen. Wenn der Build-Prozess nicht stabil ist, können erforderliche Pakete außerdem doppelten Code aus dem erforderlichen Paket enthalten, das extern sein sollte.

In unseren Projekten haben wir die Abhängigkeit von TS-Dateien aufgrund der Nachteile ausgeschlossen. Alle Abhängigkeiten zwischen den Paketen befinden sich in den index.d.ts- Dateien, daher behandelt der Compiler sie als extern und alles ist gut.

Natürlich hat .d.ts das Problem, dass ein mehrstufiger Build erforderlich ist (nicht out-of-the-box mit Tools wie Webpack möglich) und das Problem der schlechten IDE-Erfahrung (Umbenennung, Referenzen überschreiten nicht die Paketgrenzen ).

Ich stimme einigen anderen Meinungen zu - Typoskript ist ein Compiler, kein Build-System. Wir brauchen unsere Werkzeuge, um bessere Multiprojekt-Builds zu unterstützen. Ich weiß, dass es in der Community einige Möglichkeiten gibt, dies zu tun. C# verfügt beispielsweise über einen Compiler namens Roslyn und ein Build-Tool namens MSBuild.

Diskussion heute mit @mhegazy darüber, wie das Umbenennen so

Der nicht entartete Worst Case für die Umbenennung sieht so aus:

// alpha.ts
const v = { a: 1 };
export function f() { return v; }
export function g() { return v; }

// alpha.d.ts (generated)
export function f(): { a: number };
export function g(): { a: number };

// beta.ts (in another project)
import { f } from '../etc/alpha';
f().a;

// gamma.ts (in yet another project)
import { g } from '../etc/alpha';
g().a;

Die wichtigste Beobachtung ist, dass es unmöglich ist zu wissen, dass das Umbenennen von f().a g().a umbenennen sollte, es sei denn, Sie können alpha.ts um die beiden zu korrelieren.

Eine grobe Skizze des Umsetzungsplans:

  • Haben "reiche" und "schlanke" In-Memory-SourceFile-Darstellungen von .d.ts-Dateien. "Lean"-Dateien werden von der Festplatte gelesen; "reiche" werden als Ergebnis der In-Memory-.d.ts-Generierung erzeugt
  • "Rich" .d.ts SourceFiles haben Verweise von ihren Identifier Nodes auf die Ursprungsnamen(s) in der Originalquelldatei
  • Beim Umbenennen gehen wir zunächst wie gewohnt auf das fragliche Symbol. Wenn diese aus einer projektbezogenen .d.ts-Datei stammt, laden wir sie durch die Implementierung und erstellen ihre "Rich" .d.ts-Datei

    • Hinweis: Dieser Prozess ist iterativ, dh es kann erforderlich sein, mehrere Ebenen zurückzuverfolgen, bis er in einer echten Implementierungsdatei landet (eine, die aufgrund einer Projektreferenz keine .d.ts-Weiterleitung ist).

  • Verwenden Sie nun die "rich"-Zeiger, um herauszufinden, welche anderen Kennungen in den .d.ts des Projekts von derselben Quelldateikennung stammen
  • Suchen Sie in jedem Downstream-Projekt nach dem umbenannten Bezeichner und prüfen Sie, ob das Ergebnis "go-to-def" eine der "vom gleichen Symbol gestarteten" Positionen in der .d.ts . ist
  • Führen Sie die Umbenennung in der Implementierungsdatei durch

@RyanCavanaugh wird zur Definition gehen / finden, dass alle Referenzen mit diesem Modell funktionieren?

Notizen aus einer früheren Diskussion mit Anders und Mohamed zu einer großen Anzahl offener Fragen

  • Gilt prepend auch für .d.ts ? Jawohl
  • Was machen wir mit @internal in der Dogfood-Branche? Wir müssen die internen Deklarationen in den lokalen .d.ts-Dateien beibehalten, möchten aber nicht, dass sie in den Ausgabeversionen erscheinen

    • --stripInternal entfernen

    • Aber verurteile es nicht (noch...?)

    • Ryan soll remove-internal Werkzeug schreiben (fertig)

    • built -> LKG-Prozess entfernt die @internal Deklarationen

  • Was passiert, wenn Sie eine .d.ts-Datei von zB @types ändern?

    • Sie müssen manuell eine Neuerstellung erzwingen, wenn Sie mögliche neue Fehler sehen möchten ☹️

    • Könnte eventuell eine Datei "Dateien, die ich mir angesehen habe.txt" in den Ausgabeordner schreiben, wenn dies wirklich problematisch wird

  • Ist noEmitOnError obligatorisch? Jawohl.

    • Andernfalls würden Neuaufbauten Fehler verbergen!

  • referenceTarget -> composable 🚲 🏡 ✨
  • Was ist mit Leaf-Node-Projekten, bei denen keine Deklaration ausgegeben werden soll, die aber schnell neu erstellt werden sollen?

    • tsbuild oder eine gleichwertige Person können überprüfen, ob sie die nicht-upstream-relevanten Anforderungen von composable erfüllen

  • Zirkularverweise, (wie) funktionieren sie?

    • Standardmäßig nein

    • Sie können zB { path: "../blah", circular: true } angeben, wenn Sie dies tun möchten

    • Wenn Sie dies tun, liegt es an Ihnen, sicherzustellen, dass Ihr Build deterministisch ist und immer einen festen Punkt erreicht (möglicherweise nicht?!).

    • Die Neuzuordnung eines Circular:true- Imports ist eine optionale (aber priorisierte) Neuzuordnung

Sonstiges

  • @weswigham hat eine alternative Idee für die Umbenennung, die wir mit @mhegazy besprechen

Ich habe schon verloren. Ich wollte hauptsächlich nur die Interpretation von Sourcemaps aus dem Compiler heraushalten (getrennte Verantwortlichkeiten für separate Tools), aber während Sie weg waren, habe ich trotzdem daran gearbeitet, sie hinzuzufügen (weil anscheinend eine nahtlose Go-to-Def-Definition wünschenswert ist).

@RyanCavanaugh
Sollte das Umbenennen/Suchen aller Referenzen in referenzierten Projekten nach dem Zusammenführen von #23944 funktionieren? Sollten wir auch composite: true und projectReferences: [] , falls nur Sprachdienste (aber nicht tsbuild) benötigt werden?

Sollte das Umbenennen/Suchen aller Referenzen in referenzierten Projekten nach dem Zusammenführen von #23944 funktionieren?

noch nicht. aber daran arbeiten wir als nächstes.

Sollten wir auch composite: true und projectReferences: [] verwenden, falls nur Sprachdienste (aber nicht tsbuild) benötigt werden?

Ich bin mir nicht sicher, ob ich die Frage verstehe. Was meinst du mit "Sprachdienst" und nicht "Build"?

Ich bin mir nicht sicher, ob ich die Frage verstehe. Was meinst du mit "Sprachdienst" und nicht "Build"?

Ich bin nur in Editor - Unterstützung (Umbenennungs / finden Sie alle Verweise / etc ...) über mehrere Projekte in monorepo, nicht im neuen Build - Tool (auch bekannt als interessiert build mode ) (# 22997) , da ich verwende Babel für meine Zusammenstellung.

Das sollte einfach funktionieren. build ist eine Opt-In-Funktion, Sie müssen sie nicht verwenden, wenn Sie dies nicht möchten. Ähnlich wie tsc beispielsweise für Ihre Sprachdiensterfahrung in VSCode nicht erforderlich ist.

Sie müssen jedoch wahrscheinlich mit Deklarationen und Deklarationszuordnungen erstellen, um die Metadaten zu erzeugen, die für das Funktionieren projektübergreifender Refs erforderlich sind.

Ich bin mir nicht sicher, ob ich alle Aspekte des Vorschlags richtig verstehe, aber wäre es möglich, dass einzelne Projekte nicht nach Pfad, sondern nach Namen auf andere verweisen? Das Workspace-Projekt sollte eine Möglichkeit haben, jeden Projektpfad anzugeben, ähnlich wie bei Garn-Workspaces über Globs, oder vielleicht durch Auflisten jedes einzelnen Projektnamens:

Grundsätzlich statt:

"dependencies": [
    "../common", 
    "../util"
],

Können wir bitte haben?

"dependencies": [
    "common", 
    "util"
],

und haben einen Arbeitsbereich tsconfig.json

"workspace": {
  "common": "packages/common",
  "util": "packages/util"
}

Oder noch besser, Pfadsyntax:

"workspace": {
  "*":"packages/*"
}

Leistungen:

  • Möglichkeit, je nach Modulsystem unterschiedliche Lookup-Regeln anzugeben
  • Möglichkeit, zB auf node_modules zurückzugreifen, wenn das Paket außerhalb des Arbeitsbereichs heruntergeladen wird
  • Möglichkeit, mehrere Arbeitsbereiche frei zu aggregieren, um daran zu arbeiten, indem Sie mehrere Repositorys klonen und eine unterschiedliche Konfiguration für die Pfade verwenden, sodass Sie noch mehr Pakete parallel bearbeiten können.

Oder können wir zumindest die Nicht-Pfadnamen (die nicht mit './' oder '../' beginnen) für die zukünftige Verwendung reservieren...

Ich bin mir nicht sicher, inwieweit das zusammenhängt, aber Yarn 1.7 hat kürzlich ein Konzept von "fokussierten Arbeitsbereichen" eingeführt, siehe diesen Blogbeitrag .

Ist hier jemand mit beiden Arbeitsbereichen und der Arbeit von @RyanCavanaugh rund um TypeScript-Projektreferenzen / Build-Modus vertraut genug, um vielleicht einen Kommentar zu

Ich würde mich sehr über ein Update zum Fortschritt dieser Funktion freuen. Wir planen, Aurelia vNext im nächsten Monat oder so in ein Monorepo zu verschieben. Unsere neue Version ist 100% TypeScript und wir würden gerne ein offizielles TS-Projektsystem anstelle von Lerna verwenden, wenn wir können. Wir freuen uns auch, Early Adopter/Tester der neuen Features zu sein :)

Core-Unterstützung und goto def mit Source-Map- Unterstützung wurden im letzten Release (TS 2.9) hinzugefügt. tsc --b für Build-Support ist bereits vorhanden und für TS 3.0 gebunden. Die Typoskript-Codebasis ist umgezogen , um sie zu verwenden. Wir testen diese Unterstützung derzeit mit dem Typescript-Repo.

Was an dieser Stelle noch zu tun ist: 1. Alle Referenzen finden/umbenennen, um an Multiprojekt-Szenarien zu arbeiten. 2. Adressierung der Aktualisierung von .d.ts-Dateien im Hintergrund im Editor und 3. --watch Unterstützung für Multiprojekt-Szenarien. auch viele, viele Tests.

Dieses Ticket ist seit 3 ​​Jahren in seinen Büchern. Noch 3/6 ausstehende Artikel?

@claudeduguay Dies ist eine grundlegende Änderung der von TypeScript unterstützten Projekte. Zeit zum Feiern,

@mhegazy Das sind großartige Neuigkeiten. Ich freue mich sehr zu hören, dass das TS-Team es auch in seinem eigenen Projekt Dogfooding macht. Ich freue mich darauf, dass die letzten Dinge fertig werden und dies als Option für Aurelia verfügbar ist :) Sobald es eine Dokumentation zur Einrichtung gibt, werden wir unser vNext auf das neue Projektsystem umziehen. Kann es kaum erwarten!

@mhegazy Können Sie etwas Licht darauf werfen, wie dies alles mit package.json-Dateien und -Projekten basierend auf ES2015-Modulen funktionieren würde? In Aurelia vNext haben wir beispielsweise Pakete wie @aurelia/kernel , @aurelia/runtime , @aurelia/jit usw. Dies sind die Modulnamen, die in import Aussagen in den verschiedenen Projekten. Wie wird der TS-Compiler verstehen, dass diese Modulnamen den verschiedenen referenzierten Ordnern zugeordnet sind? Wird es package.json-Dateien aufnehmen, die sich in jedem referenzierten Ordner befinden? Wie unterscheidet sich dies von Lerna oder Yarn Workspaces? Mein erster Blick auf die obigen Links lässt mich denken, dass ich TS-Projekte (Build) in Kombination mit Lerna (Dep Linking and Publishing) verwenden muss, um eine funktionierende Lösung zu erhalten, aber ich sehe nicht, wie TS richtig erstellt wird wenn es nicht in package.json und node_modules integriert werden kann. Die TS-Repo-Quelle ist ganz anders als Ihr durchschnittliches Lerna-Projekt (nicht wirklich vergleichbar), also frage ich mich, ob dies unseren Anforderungen gerecht werden kann. Alle weiteren Informationen, die Sie bereitstellen können, und insb. ein funktionierendes Demo-Lösungs-Setup ähnlich dem, was ich hier beschrieben habe, wäre sehr hilfreich. Vielen Dank!

Ich teile die gleichen Fragen wie @EisenbergEffect. Insbesondere hoffe ich auch, dass dies mit einem lerna -verwalteten Monorepo gut funktioniert.

Zwei zu berücksichtigende Monorepo-Szenarien

Beginnen wir mit einem Setup, bei dem Sie alles mit lerna symbolisiert haben:

/packages
  /a
    /node_modules
      /b -> symlink to b with package.json "types" pointing to dist/index.d.ts
  /b
    /dist
      /index.d.ts -> built output of entry point declaration file

Das Wichtigste, was wir hier tun wollen, ist, b wir gebaut haben, a wieder aufzubauen, falls a nicht mehr aktuell ist. Also würden wir "references": [ { "path": "../b" } ] zu a tsconfig.json hinzufügen und tsc --build in a ausführen, um korrekte Upstream-Builds von b . In dieser Welt dienen Projektreferenzen einfach als Darstellung des Build-Graphen und ermöglichen intelligentere Inkrement-Rebuilds. Idealerweise könnten lerna und TS hier kooperieren und die package.json Abhängigkeiten gegebenenfalls in tsconfig.json spiegeln.

Ein anderes Szenario (wahrscheinlich weniger verbreitet) wäre, wenn Sie keine Symlinks erstellen, aber dennoch "so tun möchten", als würden Sie an einem Live-Paket von Paketen arbeiten. Sie können dies heute mit einer ziemlich mühsamen Pfadzuordnung tun, und einige Leute tun es. Projektreferenzen hier würden in ähnlicher Weise bei der Build-Reihenfolge helfen, aber es wäre sehr wünschenswert, eine Eigenschaft in der referierenden tsconfig-Datei zu unterstützen, um automatisch eine Pfadzuordnung zu erstellen, wenn darauf verwiesen wird; zB wenn du hättest

{
  "compilerOptions": { "outDir": "bin" },
  "package": "@RyanCavanaugh/coolstuff"
}

dann würde das Hinzufügen von "references": [{ "path": "../cool" }] automatisch eine Pfadzuordnung von @RyanCavanaugh/coolstuff -> ../cool/bin/ hinzufügen. Wir haben dies noch nicht hinzugefügt, können es aber prüfen, wenn sich herausstellt, dass es ein häufigeres Szenario ist.

Idealerweise könnten lerna und TS hier zusammenarbeiten und die package.json-Abhängigkeiten gegebenenfalls in tsconfig.json spiegeln

Anstatt uns auf externe Tools zu verlassen, könnten wir uns dafür entscheiden, Ihre package.json (sofern sie sich neben Ihrer tsconfig befinden) als potenzielle Referenzen zu lesen, wenn composite: true gesetzt ist (prüfen Sie, ob jedes aufgelöste Paket ein tsconfig.json , falls vorhanden, betrachten Sie ihn als baubaren Projektknoten und wiederholen Sie ihn). Da alles symbolisch verknüpft ist, sollten wir nicht einmal die Auflösung (viel? irgendwelche?) ändern müssen, um den Arbeitsbereich zu handhaben. Lerna richtet keine ts-spezifischen (oder build-spezifischen) Dinge ein, wie es afaik ist, es verknüpft einfach alles an Ort und Stelle und verwaltet die Versionsverwaltung. Dies wäre eine Optimierung gegenüber dem, was wir heute tun, nämlich die ts Dateien zu laden (da wir diese Deklarationen vorziehen) und alles unabhängig von der Aktualität neu zu kompilieren.

@RyanCavanaugh Das klingt ziemlich aufregend. Haben Sie eine Idee, ob es mit Rushs Symlinking-Strategie funktioniert? Kurz gesagt erstellt Rush ein synthetisches Paket Repository enthält. Dann verwenden wir pnpm , um einen einzelnen Installationsvorgang für dieses synthetische Paket durchzuführen. (PNPM verwendet Symlinks, um einen gerichteten azyklischen Graphen anstelle der Baumstruktur von NPM zu erstellen, wodurch doppelte Bibliotheksinstanzen eliminiert werden). Dann erstellt Rush für jedes Projekt im Repository einen Ordner symbolischen Links besteht , die auf die entsprechenden Ordner unter common/ temp verweisen. Das Ergebnis ist vollständig kompatibel mit TypeScript und dem standardmäßigen NodeJS-Auflösungsalgorithmus. Es ist sehr schnell, weil es eine Shrinkwrap-Datei und eine Versionsgleichung für das gesamte Repository gibt, während jedes Paket seine eigenen Abhängigkeiten angeben kann.

Wir setzen für dieses Modell nichts Besonderes in tsconfig.json . Wenn für die Funktion goto-definition eine spezielle TypeScript-Konfiguration erforderlich ist, möchten wir sie idealerweise während der Installation automatisch generieren, anstatt sie in Git zu speichern.

@pgonzal Danke für den Link zu Rush! Das hatte ich noch nicht gesehen. Ich werde es heute Abend überprüfen.

@RyanCavanaugh Danke für die Erklärung. Ihr erstes Szenario mit lerna kommt dem am nächsten, was wir hätten. Hier ist unser UX-Repo mit TS und lerna als Beispiel dafür, dass wir die neue Projektunterstützung auf https://github.com/aurelia/ux verwenden möchten

@weswigham Klingt so, als würde das, was Sie beschreiben, auch in unser Szenario passen. Beispiel-Repository oben.

Nur eine Anmerkung, dass im Fall von Garn-Workspaces Module nicht im Verzeichnis jedes einzelnen Pakets symbolisiert sind, sondern im Toplevel-Workspace node_modules .

Deshalb denke ich, dass Verweise, die nicht mit einem Punkt beginnen ('./' oder '../') für die Zukunft reserviert sein sollten. Hoffentlich werden diese als "benannte Referenzen" bezeichnet, die über die aktive Modulauflösungsstrategie behandelt werden, anstatt als relative Pfade behandelt zu werden.

@spion wir verwenden dafür bei path (zB "references": [ { "module": "@foo/baz" } ] ); Ich möchte keine Verwirrung stiften, wobei "bar" und "./bar" dasselbe in files bedeuten, aber in references etwas anderes bedeuten

Docs/Blogpost in Arbeit unten (wird dies basierend auf Feedback bearbeiten)

Ich würde jedem empfehlen, der diesem Thread folgt, es auszuprobieren. Ich arbeite jetzt an dem Monorepo-Szenario, um die letzten Fehler / Funktionen dort auszubügeln und sollte bald eine Anleitung dazu haben


Projektreferenzen

Projektreferenzen sind eine neue Funktion in TypeScript 3.0, mit der Sie Ihre TypeScript-Programme in kleinere Teile strukturieren können.

Auf diese Weise können Sie die Buildzeiten erheblich verkürzen, eine logische Trennung zwischen Komponenten erzwingen und Ihren Code auf neue und bessere Weise organisieren.

Wir führen auch einen neuen Modus für tsc , das --build Flag, das Hand in Hand mit Projektreferenzen arbeitet, um schnellere TypeScript-Builds zu ermöglichen.

Ein Beispielprojekt

Schauen wir uns ein ziemlich normales Programm an und sehen, wie Projektreferenzen uns helfen können, es besser zu organisieren.
Stellen Sie sich vor, Sie haben ein Projekt mit zwei Modulen, converter und units und jeweils einer entsprechenden Testdatei:

/src/converter.ts
/src/units.ts
/test/converter-tests.ts
/test/units-tests.ts
/tsconfig.json

Die Testdateien importieren die Implementierungsdateien und führen einige Tests durch:

// converter-tests.ts
import * as converter from "../converter";

assert.areEqual(converter.celsiusToFahrenheit(0), 32);

Zuvor war diese Struktur ziemlich umständlich, wenn Sie eine einzelne tsconfig-Datei verwendet haben:

  • Es war möglich, dass die Implementierungsdateien die Testdateien importieren
  • Es war nicht möglich, test und src gleichzeitig zu erstellen, ohne dass src im Ausgabeordnernamen angezeigt wurde, was Sie wahrscheinlich nicht möchten
  • Das Ändern nur der Interna in den Implementierungsdateien erforderte eine erneute Typprüfung der Tests, obwohl dies nie zu neuen Fehlern führen würde
  • Das Ändern nur der Tests erforderte eine erneute Typprüfung der Implementierung, auch wenn sich nichts geändert hat

Sie könnten mehrere tsconfig-Dateien verwenden, um einige dieser Probleme zu lösen, aber es würden neue erscheinen:

  • Es gibt keine integrierte aktuelle Überprüfung, sodass Sie am Ende immer tsc zweimal ausführen
  • Das zweimalige Aufrufen von tsc verursacht mehr Startzeit-Overhead
  • tsc -w kann nicht auf mehreren Konfigurationsdateien gleichzeitig ausgeführt werden

Projektreferenzen können all diese Probleme und mehr lösen.

Was ist eine Projektreferenz?

tsconfig.json Dateien haben eine neue Top-Level-Eigenschaft, references . Es ist ein Array von Objekten, das Projekte angibt, auf die verwiesen werden soll:

{
    "compilerOptions": {
        // The usual
    },
    "references": [
        { "path": "../src" }
    ]
}

Die path Eigenschaft jeder Referenz kann auf ein Verzeichnis verweisen, das eine tsconfig.json Datei enthält, oder auf die Konfigurationsdatei selbst (die einen beliebigen Namen haben kann).

Wenn Sie auf ein Projekt verweisen, passieren neue Dinge:

  • Beim Importieren von Modulen aus einem referenzierten Projekt wird stattdessen dessen Ausgabedeklarationsdatei geladen ( .d.ts )
  • Wenn das referenzierte Projekt ein outFile , sind die Deklarationen der Ausgabedatei .d.ts in diesem Projekt sichtbar
  • Der Build-Modus (siehe unten) erstellt das referenzierte Projekt bei Bedarf automatisch

Durch die Aufteilung in mehrere Projekte können Sie die Geschwindigkeit der Typprüfung und Kompilierung erheblich verbessern, den Speicherverbrauch bei der Verwendung eines Editors reduzieren und die Durchsetzung der logischen Gruppierungen Ihres Programms verbessern.

composite

Für referenzierte Projekte muss die neue Einstellung composite aktiviert sein.
Diese Einstellung ist erforderlich, um sicherzustellen, dass TypeScript schnell feststellen kann, wo die Ausgaben des referenzierten Projekts zu finden sind.
Das Aktivieren des Flags composite ändert einige Dinge:

  • Die Einstellung rootDir , wenn sie nicht explizit festgelegt wurde, standardmäßig das Verzeichnis, das die Datei tsconfig enthält
  • Alle Implementierungsdateien müssen mit einem include Muster übereinstimmen oder im files Array aufgeführt sein. Wenn diese Einschränkung verletzt wird, informiert Sie tsc , welche Dateien nicht angegeben wurden
  • declaration muss aktiviert sein

declarationMaps

Wir haben auch Unterstützung für Deklarationsquellzuordnungen hinzugefügt.
Wenn Sie --declarationMap aktivieren, können Sie Editorfunktionen wie "Gehe zu Definition" und Umbenennen verwenden, um in unterstützten Editoren transparent über Projektgrenzen hinweg zu navigieren und Code zu bearbeiten.

prepend mit outFile

Sie können das Voranstellen der Ausgabe einer Abhängigkeit auch mit der Option prepend in einer Referenz aktivieren:

   "references": [
       { "path": "../utils", "prepend": true }
   ]

Wenn Sie ein Projekt voranstellen, wird die Ausgabe des Projekts über die Ausgabe des aktuellen Projekts eingefügt.
Dies funktioniert sowohl für .js Dateien als auch für .d.ts Dateien, und auch Quellzuordnungsdateien werden korrekt ausgegeben.

tsc verwendet für diesen Vorgang immer nur vorhandene Dateien auf der Festplatte, daher ist es möglich, ein Projekt zu erstellen, bei dem keine korrekte Ausgabedatei generiert werden kann, da die Ausgabe einiger Projekte in der resultierenden Datei mehr als einmal vorhanden wäre .
Zum Beispiel:

  ^ ^ 
 /   \
B     C
 ^   ^
  \ /
   D

In dieser Situation ist es wichtig, nicht jeder Referenz voranzustellen, da Sie am Ende zwei Kopien von A in der Ausgabe von D - dies kann zu unerwarteten Ergebnissen führen.

Vorbehalte für Projektreferenzen

Projektreferenzen haben einige Kompromisse, die Sie beachten sollten.

Da abhängige Projekte .d.ts Dateien verwenden, die aus ihren Abhängigkeiten erstellt wurden, müssen Sie entweder bestimmte Build-Ausgaben einchecken oder ein Projekt nach dem Klonen erstellen, bevor Sie in einem Editor durch das Projekt navigieren können, ohne störende Fehler zu sehen Fehler.
Wir arbeiten an einem .d.ts-Generierungsprozess hinter den Kulissen, der in der Lage sein sollte, dies zu mildern, aber vorerst empfehlen wir, Entwickler zu informieren, dass sie nach dem Klonen erstellen sollten.

Um die Kompatibilität mit bestehenden Build-Workflows zu wahren, erstellt tsc nicht automatisch Abhängigkeiten, es sei denn, es wird mit dem Schalter --build aufgerufen.
Lassen Sie uns mehr über --build erfahren.

Build-Modus für TypeScript

Eine lang erwartete Funktion sind intelligente inkrementelle Builds für TypeScript-Projekte.
In 3.0 können Sie das Flag --build mit tsc .
Dies ist praktisch ein neuer Einstiegspunkt für tsc , der sich eher wie ein Build-Orchestrator als wie ein einfacher Compiler verhält.

Das Ausführen von tsc --build (kurz tsc -b ) bewirkt Folgendes:

  • Finden Sie alle referenzierten Projekte
  • Erkennen, ob sie auf dem neuesten Stand sind
  • Bauen Sie veraltete Projekte in der richtigen Reihenfolge auf

Sie können tsc -b mit mehreren Konfigurationsdateipfaden versehen (zB tsc -b src test ).
Genau wie bei tsc -p ist die Angabe des Konfigurationsdateinamens selbst unnötig, wenn er tsconfig.json .

tsc -b Befehlszeile

Sie können eine beliebige Anzahl von Konfigurationsdateien angeben:

 > tsc -b                                # Build the tsconfig.json in the current directory
 > tsc -b src                            # Build src/tsconfig.json
 > tsc -b foo/release.tsconfig.json bar  # Build foo/release.tsconfig.json and bar/tsconfig.json

Machen Sie sich keine Sorgen um die Reihenfolge der Dateien, die Sie auf der Befehlszeile übergeben - tsc ordnet sie bei Bedarf neu an, sodass Abhängigkeiten immer zuerst erstellt werden.

Es gibt auch einige Flags, die spezifisch für tsc -b :

  • --verbose : Gibt eine ausführliche Protokollierung aus, um zu erklären, was vor sich geht (kann mit jedem anderen Flag kombiniert werden)
  • --dry : Zeigt an, was getan werden würde, erstellt aber nichts
  • --clean : Löscht die Ausgaben der angegebenen Projekte (kann mit --dry kombiniert werden)
  • --force : Verhalten Sie sich so, als ob alle Projekte veraltet wären
  • --watch : Watch-Modus (kann nicht mit anderen Flags außer --verbose kombiniert werden)

Vorbehalte

Normalerweise erzeugt tsc bei Syntax- oder Typfehlern Ausgaben ( .js und .d.ts ), es sei denn, noEmitOnError ist aktiviert.
Dies in einem inkrementellen Build-System zu tun, wäre sehr schlecht - wenn eine Ihrer veralteten Abhängigkeiten einen neuen Fehler hätte, würden Sie ihn nur einmal sehen, da ein nachfolgender Build das Erstellen des jetzt aktuellen Projekts überspringen würde.
Aus diesem Grund verhält sich tsc -b effektiv so, als ob noEmitOnError für alle Projekte aktiviert wäre.

Wenn Sie Build-Ausgaben einchecken ( .js , .d.ts , .d.ts.map usw.), müssen Sie möglicherweise nach einer bestimmten Quellcodeverwaltung einen --force Build ausführen abhängig davon, ob Ihr Quellcodeverwaltungstool Zeittabellen zwischen der lokalen Kopie und der Remotekopie beibehält.

msbuild

Wenn Sie ein msbuild-Projekt haben, können Sie den Build-Modus aktivieren, indem Sie hinzufügen

    <TypeScriptBuildMode>true</TypeScriptBuildMode>

in Ihre proj-Datei. Dies ermöglicht sowohl den automatischen inkrementellen Build als auch die Bereinigung.

Beachten Sie, dass wie bei tsconfig.json / -p vorhandene TypeScript-Projekteigenschaften nicht berücksichtigt werden - alle Einstellungen sollten mit Ihrer tsconfig-Datei verwaltet werden.

Einige Teams haben msbuild-basierte Workflows eingerichtet, bei denen tsconfig-Dateien dieselbe implizite Diagrammreihenfolge aufweisen wie die verwalteten Projekte, mit denen sie gepaart sind.
Wenn Ihre Lösung so ist, können Sie weiterhin msbuild mit tsc -p zusammen mit Projektreferenzen verwenden; diese sind vollständig interoperabel.

Orientierungshilfe

Gesamtstruktur

Bei mehr tsconfig.json Dateien möchten Sie normalerweise die Vererbung von
Auf diese Weise können Sie eine Einstellung in einer Datei ändern, anstatt mehrere Dateien bearbeiten zu müssen.

Eine weitere bewährte Methode ist es, eine "Lösungsdatei" tsconfig.json , die einfach references für alle Ihre Blattknotenprojekte enthält.
Dies stellt einen einfachen Einstiegspunkt dar; zB führen wir im TypeScript-Repository einfach tsc -b src , um alle Endpunkte zu erstellen, da wir alle Unterprojekte in src/tsconfig.json
Beachten Sie, dass es ab 3.0 kein Fehler mehr ist, ein leeres files Array zu haben, wenn Sie mindestens ein reference in einer tsconfig.json Datei haben.

Sie können diese Muster im TypeScript-Repository sehen - siehe src/tsconfig_base.json , src/tsconfig.json und src/tsc/tsconfig.json als wichtige Beispiele.

Strukturierung für relative Module

Im Allgemeinen ist nicht viel erforderlich, um ein Repo mit relativen Modulen zu übertragen.
Platzieren Sie einfach eine tsconfig.json Datei in jedem Unterverzeichnis eines gegebenen übergeordneten Ordners und fügen Sie reference s zu diesen Konfigurationsdateien hinzu, um der beabsichtigten Schichtung des Programms zu entsprechen.
Sie müssen entweder outDir auf einen expliziten Unterordner des Ausgabeordners setzen oder rootDir auf das gemeinsame Stammverzeichnis aller Projektordner setzen.

Strukturierung für outFiles

Das Layout für Kompilierungen mit outFile ist flexibler, da relative Pfade nicht so wichtig sind.
Beachten Sie, dass Sie im Allgemeinen prepend bis zum "letzten" Projekt nicht verwenden möchten - dies verbessert die Build-Zeiten und reduziert die Menge an E/A, die in einem bestimmten Build erforderlich ist.
Das TypeScript-Repository selbst ist hier eine gute Referenz - wir haben einige "Bibliotheks"-Projekte und einige "Endpunkt"-Projekte; "Endpoint"-Projekte werden so klein wie möglich gehalten und ziehen nur die Bibliotheken ein, die sie benötigen.

Strukturierung für Monorepos

TODO: Experimentiere mehr und finde das heraus. Rush und Lerna scheinen unterschiedliche Modelle zu haben, die auf unserer Seite unterschiedliche Dinge implizieren

Suchen Sie auch nach Feedback zu #25164

@RyanCavanaugh Sehr schöne Beschreibung und das ausgezeichnete Feature, es wäre wirklich schön, es auszuprobieren, insb. nachdem ich Tage damit verbracht habe, unser großes Projekt in Unterprojekte mit Konfigurationsdateireferenzen zu organisieren.

Ich habe ein paar Anmerkungen:

  1. Was ist ein Monorepo? Es wäre schön, diesen Anwendungsfall etwas genauer zu beschreiben.
  2. In den meisten Fällen (insbesondere bei großen Projekten) werden während des Builds viele zusätzliche Artefakte generiert. In unserem Fall sind dies CSS, HTML, Bilddateien usw. per gulp. Ich frage mich, wie sich die Verwendung solcher Build-Tools an diese neue Vorgehensweise anpassen würde. Angenommen, ich möchte Watch nicht nur auf *.ts-Dateien ausführen, sondern auf alle unsere anderen Dateien (Stile, Markup usw.). Wie geht das? Müssen Sie parallel gulp watch und tsc -b -w ausführen?

@vvs a monorepo ist eine Sammlung von NPM-Paketen, die normalerweise von einem Tool wie Rush oder Lerna verwaltet werden

Wenn Sie gulp verwenden, sollten Sie einen Loader verwenden, der Projektreferenzen nativ versteht, um die beste Erfahrung zu erzielen. @rbuckton hat hier einige Arbeit geleistet, da einige Entwickler intern eine gulpfile verwenden; vielleicht kann er abwägen, wie gute Muster dort aussehen

@RyanCavanaugh Das sieht gut aus. Ich interessiere mich sehr für die Lerna-Anleitung :)

@RyanCavanaugh das sieht toll aus, ich arbeite gerade daran, es mit unserem lerna monorepo auszuprobieren.

Das einzige, was mir in Ihrem Bericht unklar war, war die Option prepend . Ich habe nicht ganz verstanden, welches Problem es anspricht, in welcher Situation Sie es verwenden möchten und was passiert, wenn Sie es nicht verwenden.

Das ist fantastisch! Ich arbeite an ts-loader und verwandten Projekten. Sind wahrscheinlich Änderungen erforderlich, um dies in Projekten zu unterstützen, die LanguageServiceHost / WatchHost TypeScript verwenden?

(Ein Beispiel dafür, was ich meine, finden Sie unter https://github.com/TypeStrong/ts-loader/blob/master/src/servicesHost.ts.)

Wenn ja, werden alle Anleitungen / PRs dankbar angenommen! Wenn Sie möchten, dass dies in der Webpack-Welt getestet wird, helfe ich Ihnen gerne dabei, eine Version von ts-loader herauszubringen, die dies unterstützt.

Wenn es "einfach funktioniert" ist das natürlich noch besser :smile:

Gute Arbeit!

@yortus @EisenbergEffect Ich habe unter https://github.com/RyanCavanaugh/learn-a ein Beispiel-Lerna-Repo mit einer README- Datei eingerichtet, in der die Schritte beschrieben sind, die ich unternommen habe, um es zum Laufen zu bringen.

Wenn ich das richtig verstehe, wird tsc -b X nichts tun, wenn alles (X und alle seine Abhängigkeiten und transitiven Abhängigkeiten) auf dem neuesten Stand ist? Sie fragen sich, ob wir das auch ohne das -b Flag für einzelne Projekte ohne Referenzen erreichen könnten? (in diesem Fall natürlich abzüglich der Abhängigkeiten)

Das ist ziemlich cool. Ich neige dazu, eine Lerna-Konfiguration wie diese zu verwenden (um das Mono-Repo nach Funktion zu trennen). Ich vermute, das würde genauso gut funktionieren.

{
"lerna": "2.11.0",
"Pakete": [
"Pakete/Komponenten/ ","Pakete/Bibliotheken/ ",
"Pakete/Frameworks/ ","Pakete/Anwendungen/ ",
"Pakete/Werkzeuge/*"
],
"version": "0.0.0"
}

Das ist also auf typescript@next verfügbar?

Ich werde dies mit unserem Garn-Workspace-Repository testen. Wir müssen nohoist für ein paar Module verwenden, die noch keine Arbeitsbereiche unterstützen, also ist es schön zu sehen, wie es damit umgeht.

@RyanCavanaugh Ich habe das Repo heute Abend für einen Testlauf genommen. Ich habe ein Problem im Repo geöffnet, um einige Probleme zu melden, die ich hatte. Nochmals vielen Dank für die Zusammenstellung. Ich freue mich darauf, es bald zu benutzen.

Wirklich interessant! Derzeit verwenden wir in meinem Unternehmen mein eigenes Tool namens mtsc , um den

Projekte haben unterschiedliche Konfigurationen wie; ECMA-Targeting (es5, es6), Typen (node, jest, DOM usw.), emit (einige verwenden Webpack und andere kompilieren selbst in js). Sie alle haben eines gemeinsam, und das ist das tslint-Plugin , der Rest kann ganz unterschiedlich sein. Mein Tool führt tslint auch nach einer Projektkompilierung aus (pro Projekt und wird gestoppt, wenn ein Projekt neu kompiliert wird, bevor tslint abgeschlossen ist).

Mein Hauptanliegen bei dem aktuellen Vorschlag ist, dass Sie nicht sagen können, welche Projekte welche Ressourcen teilen. Wir haben ein Server- und ein Client-Projekt, die beide einen speziellen Dienstprogrammordner verwenden, aber die Kompilierungsfehler nicht zweimal sehen wollen. Aber das lässt sich mit einem Filter beheben, das ist also kein Problem :)

Ich habe den neuen --build Modus mit unserem lerna monorepo ausprobiert, das derzeit aus 17 voneinander abhängigen Paketen besteht. Es hat eine Weile gedauert, bis alles funktionierte, aber jetzt funktioniert alles, und die Möglichkeit, inkrementell zu bauen, ist für uns eine große Verbesserung.

Ich bin auf einige Probleme gestoßen, die ich unten beschreibe. Ich hoffe, dies ist ein nützliches Feedback für das TS-Team und könnte anderen helfen, den --build Modus für ihre Projekte zum Laufen zu bringen.

Feedback für den tsc --build Modus

1. Falsch "\ist veraltet, weil die Ausgabedatei '2.map' nicht existiert"

Ich habe diese Nachricht für jedes Paket in jedem Build erhalten, sodass jeder Build ein vollständiger Neuaufbau wurde, auch wenn nichts geändert wurde. Ich habe bemerkt, dass @RyanCavanaugh dies bereits in #25281 behoben hat, daher ist es kein Problem mehr, wenn Sie auf 20180628 oder später jede Nacht aktualisieren. Bei den folgenden Problemen wird davon ausgegangen, dass Sie pro Nacht mindestens auf 20180628 aktualisiert haben.

2. "\ist mit .d.ts-Dateien aus seinen Abhängigkeiten auf dem neuesten Stand", wenn dies nicht der Fall ist

EDIT: gemeldet bei #25337.

Um dieses Problem zu reproduzieren, richten Sie das learn-a Beispielrepo von @RyanCavanaugh gemäß seinen Anweisungen ein . Führen Sie tsc -b packages --verbose zu sehen, wie alles beim ersten Mal erstellt wird. Ändern Sie nun Zeile 1 in pkg1/src/index.ts in import {} from "./foo"; und speichern Sie sie. Führen Sie tsc -b packages --verbose erneut aus. Der Build für pkg2 wird übersprungen, obwohl pkg1 so geändert wurde, dass die Quelle von pkg2 gebrochen wird. Sie können jetzt einen roten Kringel in pkg2/src/index.ts . Erstellen Sie erneut mit tsc -b packages --force und der Build-Fehler wird angezeigt. Bei den folgenden Problemen wird davon ausgegangen, dass mit --force , um dies zu umgehen.

3. Generierte .d.ts Dateien, die in Downstream-Paketen 'Duplicate Identifier'-Build-Fehler verursachen

EDIT: gemeldet unter #25338.

Um dieses Problem zu reproduzieren, richten Sie das learn-a Beispielrepo von @RyanCavanaugh gemäß seinen Anweisungen ein . Führen Sie nun lerna add @types/node , um Node.js-Typisierungen zu allen drei Paketen hinzuzufügen. Führen Sie tsc -b packages --force , um zu bestätigen, dass es immer noch gut erstellt wird. Fügen Sie nun den folgenden Code zu pkg1/src/index.ts :

// CASE1 - no build errors in pkg1, but 'duplicate identifier' build errors in pkg2
// import {parse} from 'url';
// export const bar = () => parse('bar');

// CASE2 - no build errors in pkg1 or in downstream packages
// import {parse, UrlWithStringQuery} from 'url';
// export const bar = (): UrlWithStringQuery => parse('bar');

// CASE3 - no build errors in pkg1 or in downstream packages
// export declare const bar: () => import("url").UrlWithStringQuery;

// CASE4 - no build errors in pkg1, but 'duplicate identifier' build errors in pkg2
// import {parse} from 'url';
// type UrlWithStringQuery = import("url").UrlWithStringQuery;
// export const bar = (): UrlWithStringQuery => parse('bar');

Entkommentieren Sie jeweils einen Fall und führen Sie tsc -b packages --force . Die Fälle 1 und 4 verursachen eine Flut von Build-Fehlern in pkg2 . Bei den Fällen 2 und 3 gibt es keine Build-Fehler. Der wichtige Unterschied zu den Fällen 1 und 4 scheint die erste Zeile im generierten pkg1/lib/index.d.ts :

/// <reference path="../node_modules/@types/node/index.d.ts" />

Die Fälle 2 und 3 erzeugen diese Zeile nicht. Wenn pkg2 in Fall 1 und 4 erstellt wird, enthält es zwei identische Kopien von @types/node Deklarationen in unterschiedlichen Pfaden, und dies führt zu den Fehlern 'duplicate Identifier'.

Vielleicht ist dies beabsichtigt, da die Fälle 2 und 3 funktionieren. Es scheint jedoch ziemlich verwirrend. In pkg1 in keinem dieser 4 Fälle Build-Fehler oder Warnungen, aber das nachgelagerte Build-Verhalten ist sehr empfindlich auf den genauen Stil der exportierten Deklarationen. Ich denke, entweder (a) pkg1 sollte für die Fälle 1 und 4 einen Fehler aufweisen, oder (b) alle vier Fälle sollten das gleiche Downstream-Build-Verhalten aufweisen, oder (c) es sollte eine klare Anleitung vom TS-Team geben zu wie Sie Deklarationen schreiben, um dieses Problem zu vermeiden.

4. Probleme mit import Typen in generierten .d.ts Dateien bei Verwendung von Garn-Workspaces

Als ich versuchte, den Build-Modus mit unserem Monorepo mit 17 Paketen zum Laufen zu bringen, habe ich eine Reihe von Build-Fehlern durchgearbeitet, die durch die relativen Pfade in import Typen in generierten .d.ts Dateien verursacht wurden. Ich habe endlich herausgefunden, dass das Problem mit dem Hochziehen von Modulen zusammenhängt. Das heißt, wenn Sie Garn-Workspaces verwenden, werden alle installierten Module in das monorepo-root-Verzeichnis node_modules hochgehoben, einschließlich der symbolischen Links für alle Pakete im Monorepo. Ich habe das Monorepo so geändert, dass es die Eigenschaft packages in lerna.json , was dazu führt, dass lerna seinen eigenen nicht-hoisting Bootstrapping-Algorithmus verwendet, und das löste das Problem. Es ist sowieso ein besserer / sichererer Ansatz, wenn auch langsamer.

Ich bin mir nicht sicher, ob TS beabsichtigt, modulgestützte Setups zu unterstützen, daher habe ich kein Repro für die Probleme entwickelt, auf die ich gestoßen bin, aber ich könnte versuchen, eines zu erstellen, wenn Interesse besteht. Ich denke, das Problem kann sein, dass einige Builds den gleichen Typ sowohl über das Verzeichnis packages der obersten Ebene (gemäß den tsconfig-Einstellungen) als auch das Verzeichnis node_modules der obersten Ebene (gemäß import gibt generierte .d.ts Dateien ein). Dies funktioniert manchmal aufgrund der strukturellen Typisierung, schlägt jedoch bei Dingen wie exportierten eindeutigen Symbolen fehl.

5. Wiederholte Konfiguration

Um ein Monorepo einzurichten, um lerna zu verwenden, müssen Sie im Grunde nur etwas wie "packages": ["packages/*"] in lerna.json eingeben. Lerna berechnet die genaue Paketliste durch Erweitern von globstars und berechnet dann den genauen Abhängigkeitsgraphen, indem er sich die Abhängigkeiten ansieht, die in den package.json jedes Pakets deklariert sind. Sie können Pakete und Abhängigkeiten nach Belieben hinzufügen und entfernen, und lerna hält Schritt, ohne die Konfiguration berühren zu müssen.

Der TypeScript --build Modus beinhaltet etwas mehr Zeremonie. Glob-Muster werden nicht erkannt, daher müssen alle Pakete explizit aufgelistet und verwaltet werden (zB in packages/tsconfig.json ) in @RyanCavanaughs learn-a Beispiel-Repository. Der Build-Modus berücksichtigt keine package.json Abhängigkeiten, daher muss jedes Paket die Liste der anderen Pakete, von denen es abhängt, sowohl in seiner package.json Datei (unter "dependencies" ) als auch es ist tsconfig.json Datei (unter "references" ).

Dies ist eine kleine Unannehmlichkeit, aber ich füge sie hier ein, da ich das Gelaber im Vergleich zum Ansatz von lerna's auffällig fand.

6. tsc Absturz mit globalen Modulerweiterungen

EDIT: gemeldet bei #25339.

Um dieses Problem zu reproduzieren, richten Sie das Beispiel- Repository learn-a von @RyanCavanaugh gemäß seinen Anweisungen ein . Führen Sie nun lerna add @types/multer , um allen drei Paketen multer Eingaben hinzuzufügen. Führen Sie tsc -b packages --force , um zu bestätigen, dass es immer noch gut erstellt wird. Fügen Sie nun die folgende Zeile zu pkg1/src/index.ts :

export {Options} from 'multer';

Führen Sie tsc -b packages --force erneut aus. Der Compiler stürzt aufgrund einer verletzten Assertion ab. Ich habe mir kurz den Stack-Trace und die Assertion angeschaut, und es scheint etwas mit der globalen Erweiterung des Express Namespace zu tun zu haben.

danke @yortus für das Feedback. verlassen Sie sich darauf. für 3, ich denke, es ist https://github.com/Microsoft/TypeScript/issues/25278.

Zu 4 kenne ich mich mit dem Modulheben als Konzept nicht aus. können Sie eine Repro ausarbeiten und/oder teilen?

@mhegazy viele, die lerna und garn verwenden, verwenden Arbeitsbereiche (einschließlich mir). Mehr Infos hier: https://yarnpkg.com/lang/en/docs/workspaces/

Ich verwende derzeit Garn-Workspaces, lerna, erweiterte tsconfigs, in denen die Basis-Tsconfig deklariert, dass paths für alle Pakete mit angehobenem Modul unter root/node_modules freigegeben ist. Wenn ich yarn und monorepo höre, denke ich an workspaces weil dies die eigentliche Absicht der Funktion ist - die Verwendung zu vereinfachen und Duplikate zu reduzieren. Ich hatte erwartet, dass diese Änderung einfach meine langwierigen / schmerzhaften paths entfernen würde, die in meiner Basis-tsconfig deklariert sind.

Hier ist ein Beispiel unserer Root-Monorepo-tsconfig (falls es hilfreich ist):

{
  "extends": "./packages/build/tsconfig.base.json",
  "compilerOptions": {
    "baseUrl": "./packages",
    "paths": {
      "@alienfast/build/*": ["./build/src/*"],
      "@alienfast/common-node/*": ["./common-node/src/*"],
      "@alienfast/common/*": ["./common/src/*"],
      "@alienfast/concepts/*": ["./concepts/src/*"],
      "@alienfast/faas/*": ["./faas/src/*"],
      "@alienfast/math/*": ["./math/src/*"],
      "@alienfast/notifications/*": ["./notifications/src/*"],
      "@alienfast/ui/*": ["./ui/src/*"],
      "@alienfast/build": ["./build/src"],
      "@alienfast/common-node": ["./common-node/src"],
      "@alienfast/common": ["./common/src"],
      "@alienfast/concepts": ["./concepts/src"],
      "@alienfast/faas": ["./faas/src"],
      "@alienfast/math": ["./math/src"],
      "@alienfast/notifications": ["./notifications/src"],
      "@alienfast/ui": ["./ui/src"],
    }
  },
  "include": ["./typings/**/*", "./packages/*/src/**/*"],
  "exclude": ["node_modules", "./packages/*/node_modules"]
}

Ich werde eine Probe beim Gabeln machen:
https://github.com/RyanCavanaugh/learn-a

Hier ist eine nicht zum Zusammenführen geeignete PR für das Repo von
https://github.com/RyanCavanaugh/learn-a/pull/3/files

Wir haben auch Modulhebetechnik in

Ich verstehe Arbeitsbereiche als etwas sauberer, als den Befehl link zwischen allen Paketen verwenden zu müssen, damit sie aufeinander zugreifen können (oder zumindest auf ihre Abhängigkeiten zugreifen).

Wie oben verschiebt das Modulhoisting alle Abhängigkeiten in ein Root-Verzeichnis node_modules . Dies macht sich die Tatsache zunutze, dass die Knotenmodulauflösung immer den Verzeichnisbaum durchquert und alle node_modules Verzeichnisse durchsucht, bis das erforderliche Modul gefunden wird. Die einzelnen Module in Ihrem Monorepo werden dann in dieser Wurzel node_modules symbolisiert und alles funktioniert einfach. Der Garn-Blog-Post erklärt es wahrscheinlich besser, als ich es kann.

Das Anheben ist nicht garantiert. Wenn Sie nicht übereinstimmende Versionen desselben Pakets haben, werden diese nicht hochgezogen. Außerdem unterstützen viele vorhandene Tools das Hochziehen nicht, da sie Annahmen darüber treffen, wo node_modules wird, oder weil sie die Auflösung des Knotenmoduls nicht korrekt befolgen. Aus diesem Grund gibt es eine nohoist Einstellung, die das Hochziehen für bestimmte Module oder Abhängigkeiten deaktivieren kann.

Ich habe meinem vorherigen Feedback einen sechsten Punkt hinzugefügt. tsc stürzt in dem dort beschriebenen Szenario ab.

@mhegazy Ich bin mir nicht sicher, ob Artikel 3 mit #25278 zusammenhängt. #25278 beschreibt die Ausgabe einer ungültigen Deklaration. Meine generierten Deklarationsdateien waren syntaktisch und semantisch gültig, führten jedoch dazu, dass nachgelagerte Projekte mit zwei Kopien von Knotentypisierungen erstellt wurden, was zu Fehlern mit doppelten Bezeichnern führte.

Wie oben verschiebt das Modulhoisting alle Abhängigkeiten in ein Root-Verzeichnis node_modules. Dies macht sich die Tatsache zunutze, dass die Knotenmodulauflösung immer den Verzeichnisbaum durchquert und alle node_modules-Verzeichnisse durchsucht, bis sie das erforderliche Modul findet.

Übrigens hat dieses Modell einen Nachteil, dass es zu "Phantom-Abhängigkeiten" führt, bei denen ein Projekt eine Abhängigkeit importieren kann, die nicht explizit in seiner package.json-Datei deklariert wurde. Wenn Sie Ihre Bibliothek veröffentlichen, kann dies zu Problemen führen, wie z. B. (1) eine andere Version der Abhängigkeit als die getestete/erwartete installiert wird oder (2) die Abhängigkeit vollständig fehlt, weil sie aus einem nicht verwandten Projekt stammt, das nicht installiert ist in diesem Kontext. PNPM und Rush haben beide Architekturoptionen, die vor Phantomabhängigkeiten schützen sollen.

Ich habe eine allgemeine Frage zu tsc --build : Möchte der TypeScript-Compiler die Rolle der Orchestrierung des Builds für Projekte in einem Monorepo übernehmen? Normalerweise hat die Toolchain eine ganze Pipeline von Aufgaben, wie zum Beispiel:

  • Vorverarbeitung
  • Generieren lokalisierter Zeichenfolgen
  • Konvertieren von Assets in JavaScript (CSS, Bilder usw.)
  • Kompilieren (Typprüfung / Transpilieren)
  • Aufrollen der .js-Dateien (zB Webpack)
  • Aufrollen der .d.ts-Dateien (zB API Extractor)
  • Nachbearbeitung inklusive Unit-Tests und Dokumentationserstellung

Normalerweise verwaltet ein System wie Gulp oder Webpack diese Pipeline, und der Compiler ist nur ein Schritt in der Mitte der Kette. Manchmal führt ein sekundäres Tool den Build auch auf andere Weise aus, zB Jest+ts-jest for jest --watch .

Zielt tsc darauf ab, diese Dinge selbst zu verwalten? Und wenn nicht, gibt es für einen herkömmlichen Build-Orchestrator eine Möglichkeit, den Abhängigkeitsgraphen selbst zu lösen und zB tsc in jedem Projektordner in der richtigen Reihenfolge (nachdem die Vorverarbeitung aktualisiert wurde) wiederholt aufzurufen?

Oder wenn das Design ein komplettes Monorepo in einem einzigen Durchlauf verarbeiten soll (während wir heute jedes Projekt in einem separaten NodeJs-Prozess bauen), bin ich auch gespannt, wie sich die anderen Build-Aufgaben beteiligen: Werden wir zum Beispiel Webpack auf laufen lassen? alle Projekte auf einmal? (In der Vergangenheit führte dies zu Problemen mit unzureichendem Arbeitsspeicher.) Werden wir die Möglichkeit verlieren, die Parallelität mehrerer Prozesse auszunutzen?

Das sind übrigens keine Kritikpunkte. Ich versuche nur, das Gesamtbild und die beabsichtigte Verwendung zu verstehen.

@pgonzal richtig, es gibt viele nicht-tsc-Teile, um ein echtes Monorepo zu erstellen. Für unsere lerna monorepo habe ich folgenden Ansatz gewählt:

  • Jedes Paket im Monorepo definiert optional ein prebuild Skript und/oder ein postbuild Skript in seinem package.json . Diese enthalten die nicht-tsc-Aspekte des Builds.
  • in den package.json des Monorepos gibt es diese Skripte:
    "prebuild": "lerna run prebuild", "build": "tsc --build monorepo.tsconfig.json --verbose", "postbuild": "lerna run postbuild",
  • Das ist es. Das Ausführen von yarn build auf Monorepo-Ebene führt die prebuild Skripte für jedes Paket aus, das sie definiert, dann führt es den tsc --build Schritt aus und dann alle postbuild Skripte. (Nach Konvention sowohl in npm als auch in Garn ist das Ausführen von npm run foo ungefähr dasselbe wie npm run prefoo && npm run foo && npm run postfoo .)

Wie gehen Sie mit jest --watch oder webpack-dev-server ? Werden beispielsweise die Prebuild-/Postbuild-Schritte erneut ausgeführt, wenn eine Quelldatei geändert wird?

Hat dies Auswirkungen auf ts-node und verwandte Workflows? Einige unserer Hilfsanwendungen werden "direkt" von TypeScript ausgeführt, wie "start": "ts-node ./src/app.ts" oder "start:debug": "node -r ts-node/register --inspect-brk ./src/app.ts" .

Ein weiteres Problem mit dem Build-Modus wurde unter #25355 gemeldet.

Danke für all die tollen Rückmeldungen und Untersuchungen bisher. Ich schätze wirklich jeden, der sich die Zeit genommen hat, Dinge auszuprobieren und die Reifen zu treten.

@yortus re https://github.com/Microsoft/TypeScript/issues/3469#issuecomment -400439520

Toller Bericht, danke nochmal für die Bereitstellung. Ihre Probleme in der richtigen Reihenfolge -

  1. Fest
  2. PR bis #25370
  3. Diskussion über das protokollierte Problem - nicht sofort ersichtlich, was die richtige Lösung ist, aber wir werden etwas tun
  4. Untersuchung (unten)
  5. Geloggt #25376
  6. Technisch nicht verwandt mit --build AFAICT. Dies ist eine neue Behauptung, die wir kürzlich hinzugefügt haben. Nathan ermittelt

@rosskevin 🎉 für die PR zum learn-a Repo! Ich werde das in einem Zweig zusammenführen, damit wir besser vergleichen und kontrastieren können.

@pgonzal re https://github.com/Microsoft/TypeScript/issues/3469#issuecomment -401577442

Ich habe eine allgemeine Frage zu tsc --build: Möchte der TypeScript-Compiler die Rolle der Orchestrierung des Builds für Projekte in einem Monorepo übernehmen?

Große Frage; Darauf möchte ich ganz klar antworten: definitiv nicht .

Wenn Sie heute mit tsc zufrieden sind, um Ihr Projekt zu erstellen, möchten wir, dass Sie morgen mit tsc -b zufrieden sind, um Ihr mehrteiliges Projekt zu erstellen. Wenn Sie heute mit gulp zufrieden sind, um Ihr Projekt zu erstellen, möchten wir, dass Sie morgen mit gulp zufrieden sind, um Ihr mehrteiliges Projekt zu erstellen. Wir haben die Kontrolle über das erste Szenario, brauchen aber Tool- und Plugin-Autoren, die uns beim zweiten helfen, weshalb sogar tsc -b nur ein dünner Wrapper über exponierte APIs ist, mit denen Tool-Autoren Projektreferenzen gut spielen können in ihren Baumodellen.

Der breitere Kontext ist, dass es eine ziemlich heftige interne Debatte darüber gab, ob tsc -b überhaupt existieren oder stattdessen ein separates Tool/Einstiegspunkt sein sollte – der Aufbau eines universellen Build-Orchestrators ist eine enorme Aufgabe und keine, die wir sind sich anzumelden. Für unser eigenes Repository haben wir tsc mit einem leichten Task-Runner-Framework verwendet und verwenden jetzt tsc -b mit demselben Task-Runner, und ich würde erwarten, dass jeder andere migriert, auch seine vorhandene Build-Kette beizubehalten mit nur kleinen korrekturen.

@borekb re https://github.com/Microsoft/TypeScript/issues/3469#issuecomment -401593804

Hat dies Auswirkungen auf ts-node und verwandte Workflows? Einige unserer Hilfs-Apps laufen "direkt" von TypeScript

Für Einzeldatei-Skripts, die implizit keine Projektreferenzen haben können, gibt es keine Auswirkungen.

@EisenbergEffect hatte im learn-a Repo einige Fragen zur projektübergreifenden Umbenennung und anderen Sprachdienstfunktionen. Die große offene Frage hier ist nur, ob wir dieses Feature für 3.0 in einen brauchbaren Zustand bringen können oder nicht. Wenn ja, dann funktioniert die projektübergreifende Umbenennung "einfach", mit dem Vorbehalt, dass es für uns offensichtlich unmöglich ist, alle nachgelagerten Projekte schlüssig zu finden und zu aktualisieren - dies ist eine "beste Anstrengung" basierend auf einigen Heuristiken für die Suche nach anderen Projekte.

Wenn wir projektübergreifende Umbenennungs glaube nicht , dass in akzeptabler Weise stabil + komplett für 3,0, wir wahrscheinlich Umbenennungsoperationen nur blockieren würden , wenn das umbenannte Symbol in der .d.ts Ausgabedatei eines anderes Projektes ist - so dass Sie dies tun wäre, sehr verwirrend, da die .d.ts-Datei bei einem nachfolgenden Build des Upstream-Projekts aktualisiert wurde, nachdem das Upstream-Projekt geändert wurde war nicht wirklich aktualisiert worden.

Funktionen wie Go to Definition funktionieren heute in VS Code und werden in zukünftigen Versionen von Visual Studio sofort einsatzbereit sein. Diese Funktionen erfordern alle die Aktivierung von .d.ts.map-Dateien (aktivieren Sie declarationMap ). Es gibt einige Arbeiten pro Feature, um dies zu beleuchten. Wenn Sie also feststellen, dass etwas nicht wie erwartet funktioniert, loggen Sie einen Fehler ein, da wir einige Fälle möglicherweise übersehen haben.

Offene Probleme, die ich an dieser Stelle verfolge:

  • Projektübergreifende Umbenennung - @andy-ms implementiert
  • Ich muss die Konfigurationen von hochgezogenen Modulen analysieren und ihre Auswirkungen verstehen - auf mich
  • Es sollte eine Version des Beispiel-Repos learn-a , die yarn , und eine andere, die pnpm , und eine andere, die eine davon im angehobenen Modus verwendet

Offene Fragen

  • Sollten wir Logik implementieren, um package.json s zu lesen, um Projektreferenzen abzuleiten? Geloggt #25376
  • Sollte .d.ts.map für composite Projekte implizit eingeschaltet sein?

@RyanCavanaugh zum hinzufügen

Offene Probleme, die ich derzeit verfolge

Wir haben auch erwähnt, dass es einen inkrementellen Ausgabecache gibt, der vom tatsächlichen Ausgabespeicherort des Projekts getrennt ist, um Dinge wie das Aktualisieren von Deklarationen im Hintergrund im LS zu handhaben (heute werden Änderungen im Editor nicht über Projektgrenzen hinweg verbreitet, bis Sie bauen). stripInternal , und mutierende Build-Prozesse (wo unsere Build-Ausgaben an Ort und Stelle mutiert sind und daher nicht für LS-Operationen geeignet sind).

Entschuldigung für eine dumme Frage, da sie in der Roadmap überprüft wurde, wie kann ich diese Funktion aktivieren?

@aleksey-bykov Sie können es in typescript@next verwenden.

Ich habe dies gerade auf unserem Monorepo mit Garn-Workspace-Power ausprobiert und es funktioniert gut.

Eine Sache, die mir aufgefallen ist, war, dass tsc --build --watch Fehler meldet, dann aber nichts ausgibt, was sagt, dass der Build jetzt behoben ist. Der standardmäßige tsc Überwachungsmodus in 2.9 hat damit begonnen, einen Fehlerzähler auszugeben, und es ist schön, dort eine Null zu sehen, damit Sie wissen, dass der Bau abgeschlossen ist.

Ich habe einen Ordner voller *.d.ts und nichts anderes, was ich dagegen tun soll:

  • mache daraus ein Projekt und referenziere es (versucht, hat nicht funktioniert)
  • benutze "include" dafür

@timfish , das Feedback stimmt mit anderen

@aleksey-bykov https://github.com/Microsoft/TypeScript/issues/3469#issuecomment -400439520 sollte helfen, einige Konzepte zu erklären

@RyanCavanaugh es sieht so aus, als ob die Projektreferenzierung nur für die Auflösung von Commonjs und Knotenmodulen funktioniert, nicht wahr?

in deinem beispiel:

import * as p1 from "@ryancavanaugh/pkg1";
import * as p2 from "@ryancavanaugh/pkg2";

p1.fn();
p2.fn4();
  1. Was ist das Modul @ryancavanaugh , hat es etwas damit zu tun, wie TS Module auflöst?
  2. soll dieses Beispiel mit AMD (klassische Modulauflösung) funktionieren?
  3. ist outFile erforderlich, damit Definitionen gefunden werden?
  4. wo d.ts-Dateien sein sollen, damit das referenzierende Projekt sie findet (kann ich noch outDir verwenden? wird TS sie dort finden?)

Ich habe 2 einfache Projekte essentials und common und gemeinsame Dinge können in Essentials kompilierte Dinge nicht lösen:

image

@aleksey-bykov

  1. Es ist nur ein bereichsbezogener Modulname, der nach dem üblichen Auflösungsalgorithmus für Knotenmodule aufgelöst wird
  2. Sie können Projektreferenzen mit jedem Modulsystem einschließlich Classic verwenden, aber die Beispielnamen (bereichsbezogene Module) sind außerhalb von Knoten nicht sehr benutzerfreundlich
  3. Nein
  4. TypeScript sucht nach den .d.ts-Dateien an der Stelle, an der das referenzierte Projekt sie erstellt

Wenn Sie ein Beispiel-Repository oder ähnliches haben, kann ich diagnostizieren, warum Sie diesen Fehler erhalten

@RyanCavanaugh bitte tun
beispiel.zip

@RyanCavanaugh , es sieht auch so aus, als würde tsc --build --watch zunächst keine Dateien ausgeben, bis eine Änderung einer Quelldatei festgestellt wird.

Faden zu lang (in Zeit und Raum); Lassen Sie uns die Diskussion unter der glücklichen Ausgabenummer 100 * 2^8 aufnehmen: #25600

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

manekinekko picture manekinekko  ·  3Kommentare

blendsdk picture blendsdk  ·  3Kommentare

jbondc picture jbondc  ·  3Kommentare

remojansen picture remojansen  ·  3Kommentare

siddjain picture siddjain  ·  3Kommentare