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.
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:
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:
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.
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.
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.
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.
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:
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.
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.
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.
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.
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:
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:
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 dependencies
) einführen, wenn files
bereits existiert und in Ordnung ist?{
"compilerOptions": {
// ...
},
"files": [
"../common/tsconfig.json", // <== takes the `files` part of the tsconfig.json
"../common/tsconfig.util.json", // <==
"core.ts",
"sys.ts"
]
}
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
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.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:
.d.ts
Datei. Idealerweise führt dies Sie zur Quelle in einem anderen Projekt.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.
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).
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.
Es sind drei Hauptszenarien zu berücksichtigen.
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.
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");
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.
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.
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';
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
).
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.
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" />
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 .
Einige kritische Beobachtungen aus der Interaktion mit realen Projekten:
skipLibCheck
, sind in Bezug auf Typprüfung und Speicherkosten fast "kostenlos".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.
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.
Projektreferenzen ändern das folgende Verhalten:
.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östReferenced project "../otherProject" is not built
anstelle eines einfachen "Datei nicht gefunden"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:
tsconfig.json
eines referenzierten Projekts sollten von der Festplatte gelesen werdenUm 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 überschreibenrootDir
standardmäßig "."
(das Verzeichnis, das die tsconfig.json
Datei enthält), anstatt aus den Eingabedateien abgeleitet zu werdenfiles
Array bereitgestellt wird, muss es die Namen aller Eingabedateien bereitstellennode_modules/@types
) müssen nicht angegeben werdenreferences
Array haben (das leer sein kann)."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
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.
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:
Dieses Tool sollte nur öffentliche APIs verwenden und gut dokumentiert sein, damit die Autoren von Build-Tools verstehen, wie Projektreferenzen korrekt implementiert werden.
Abschnitte, die ausgefüllt werden müssen, um dieses Angebot vollständig zu vervollständigen
tsconfig.json
Dateien ab und fügen Sie dann Referenzen hinzu, die zum Beheben von Build-Fehlern erforderlich sindbaseUrl
dtsEmitOnly
Einstellung für Leute, die ihr JS zB durch webpack/babel/rollup leiten?references
+ noEmit
diesFantastisch!
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:
outDir
(aber natürlich declaration: true
), dann brauchen wir weder rootDir
noch references
outDir
, dann müssten Sie references
und/oder rootDir
(und declaration: true
) setzenGrund 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.
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:
skipLibCheck
sollte die Auswirkungen auf die Leistung vernachlässigbar machen, aber ich habe es nicht überprüft.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:
//# sourceURL = ../src/foo.ts
im .d.ts
.d.ts
zurück zum ursprünglichen .ts
zuzuordnen.js
Datei und Verwenden ihrer Quellzuordnung, um die `.ts . zu findenDies 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:
@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:
references
und dependencies
).@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
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:
@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
prepend
auch für .d.ts
? Jawohl@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
entfernenremove-internal
Werkzeug schreiben (fertig)@internal
Deklarationen@types
ändern?noEmitOnError
obligatorisch? Jawohl.referenceTarget
-> composable
🚲 🏡 ✨tsbuild
oder eine gleichwertige Person können überprüfen, ob sie die nicht-upstream-relevanten Anforderungen von composable
erfüllen{ path: "../blah", circular: true }
angeben, wenn Sie dies tun möchtenSonstiges
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:
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 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.
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:
test
und src
gleichzeitig zu erstellen, ohne dass src
im Ausgabeordnernamen angezeigt wurde, was Sie wahrscheinlich nicht möchtenSie könnten mehrere tsconfig-Dateien verwenden, um einige dieser Probleme zu lösen, aber es würden neue erscheinen:
tsc
zweimal ausführentsc
verursacht mehr Startzeit-Overheadtsc -w
kann nicht auf mehreren Konfigurationsdateien gleichzeitig ausgeführt werdenProjektreferenzen können all diese Probleme und mehr lösen.
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:
.d.ts
)outFile
, sind die Deklarationen der Ausgabedatei .d.ts
in diesem Projekt sichtbarDurch 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:
rootDir
, wenn sie nicht explizit festgelegt wurde, standardmäßig das Verzeichnis, das die Datei tsconfig
enthältinclude
Muster übereinstimmen oder im files
Array aufgeführt sein. Wenn diese Einschränkung verletzt wird, informiert Sie tsc
, welche Dateien nicht angegeben wurdendeclaration
muss aktiviert seindeclarationMaps
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.
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.
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:
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
BefehlszeileSie 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)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.
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.
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.
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.
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.
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:
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.
tsc --build
ModusIch 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.
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.
.d.ts
Dateien, die in Downstream-Paketen 'Duplicate Identifier'-Build-Fehler verursachenEDIT: 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.
import
Typen in generierten .d.ts
Dateien bei Verwendung von Garn-WorkspacesAls 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.
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.
tsc
Absturz mit globalen ModulerweiterungenEDIT: 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:
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:
prebuild
Skript und/oder ein postbuild
Skript in seinem package.json
. Diese enthalten die nicht-tsc-Aspekte des Builds.package.json
des Monorepos gibt es diese Skripte:
"prebuild": "lerna run prebuild",
"build": "tsc --build monorepo.tsconfig.json --verbose",
"postbuild": "lerna run postbuild",
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 -
--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:
learn-a
, die yarn
, und eine andere, die pnpm
, und eine andere, die eine davon im angehobenen Modus verwendetOffene Fragen
package.json
s zu lesen, um Projektreferenzen abzuleiten? Geloggt #25376.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:
@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();
@ryancavanaugh
, hat es etwas damit zu tun, wie TS Module auflöst?outFile
erforderlich, damit Definitionen gefunden werden?Ich habe 2 einfache Projekte essentials
und common
und gemeinsame Dinge können in Essentials kompilierte Dinge nicht lösen:
@aleksey-bykov
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
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
undunits
und jeweils einer entsprechenden Testdatei:Die Testdateien importieren die Implementierungsdateien und führen einige Tests durch:
Zuvor war diese Struktur ziemlich umständlich, wenn Sie eine einzelne tsconfig-Datei verwendet haben:
test
undsrc
gleichzeitig zu erstellen, ohne dasssrc
im Ausgabeordnernamen angezeigt wurde, was Sie wahrscheinlich nicht möchtenSie könnten mehrere tsconfig-Dateien verwenden, um einige dieser Probleme zu lösen, aber es würden neue erscheinen:
tsc
zweimal ausführentsc
verursacht mehr Startzeit-Overheadtsc -w
kann nicht auf mehreren Konfigurationsdateien gleichzeitig ausgeführt werdenProjektreferenzen 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:Die
path
Eigenschaft jeder Referenz kann auf ein Verzeichnis verweisen, das einetsconfig.json
Datei enthält, oder auf die Konfigurationsdatei selbst (die einen beliebigen Namen haben kann).Wenn Sie auf ein Projekt verweisen, passieren neue Dinge:
.d.ts
)outFile
, sind die Deklarationen der Ausgabedatei.d.ts
in diesem Projekt sichtbarDurch 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:rootDir
, wenn sie nicht explizit festgelegt wurde, standardmäßig das Verzeichnis, das die Dateitsconfig
enthältinclude
Muster übereinstimmen oder imfiles
Array aufgeführt sein. Wenn diese Einschränkung verletzt wird, informiert Sietsc
, welche Dateien nicht angegeben wurdendeclaration
muss aktiviert seindeclarationMaps
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
mitoutFile
Sie können das Voranstellen der Ausgabe einer Abhängigkeit auch mit der Option
prepend
in einer Referenz aktivieren: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:
In dieser Situation ist es wichtig, nicht jeder Referenz voranzustellen, da Sie am Ende zwei Kopien von
A
in der Ausgabe vonD
- 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
mittsc
.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
(kurztsc -b
) bewirkt Folgendes:Sie können
tsc -b
mit mehreren Konfigurationsdateipfaden versehen (zBtsc -b src test
).Genau wie bei
tsc -p
ist die Angabe des Konfigurationsdateinamens selbst unnötig, wenn ertsconfig.json
.tsc -b
BefehlszeileSie können eine beliebige Anzahl von Konfigurationsdateien angeben:
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 obnoEmitOnError
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
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
mittsc -p
zusammen mit Projektreferenzen verwenden; diese sind vollständig interoperabel.Orientierungshilfe
Gesamtstruktur
Bei mehr
tsconfig.json
Dateien möchten Sie normalerweise die Vererbung vonAuf 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 einfachreferences
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 insrc/tsconfig.json
Beachten Sie, dass es ab 3.0 kein Fehler mehr ist, ein leeres
files
Array zu haben, wenn Sie mindestens einreference
in einertsconfig.json
Datei haben.Sie können diese Muster im TypeScript-Repository sehen - siehe
src/tsconfig_base.json
,src/tsconfig.json
undsrc/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 Siereference
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 oderrootDir
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