Konfigurieren Sie ein TypeScript-Projekt mit allgemeinen Abhängigkeiten, um mehrere einfache JavaScript-Ausgabedateien zu erstellen

10

Ich schreibe gerade einige Skripte für Bot Land . Bot Land ist ein Echtzeit-Strategiespiel, bei dem Sie anstelle der Steuerung Ihrer Einheiten mit Maus und Tastatur Code schreiben, um Ihre Bots über eine API zu steuern, und dann Ihre Bots gegen die Bots anderer kämpfen. Wenn Sie mit Einheiten in SC2 vertraut sind, können Sie Bots erstellen, die Blink-Stalkern, Belagerungspanzern, Medizinern und Ultralisken ähneln. (Es ist ein ziemlich lustiges Spiel für Softwareentwickler, aber das liegt außerhalb des Rahmens dieser Frage.)

Bot Land

Die Bot-Steuerung weist drei Ebenen zunehmender Komplexität auf: eine Standard-KI, eine Scratch- ähnliche Programmiersprache und einen reduzierten JavaScript-Satz namens BotLandScript. Obwohl der integrierte Editor für BotLandScript sinnvoll ist, müssen Sie Ihren gesamten Code als eine einzige Datei mit globalen Funktionen der obersten Ebene überall hochladen . Natürlich wird dies nach einer Weile schmerzhaft, wenn Ihr Code langsam wird und verschiedene Bots die gleichen Funktionen haben.

Programmierumgebung

Um das Schreiben von Code für mehrere Bots zu vereinfachen, die Wahrscheinlichkeit unbeabsichtigter Fehler beim Codieren in Bare JS zu verringern und meine Chancen zu erhöhen, andere Spieler zu schlagen, habe ich das obige TypeScript-Projekt eingerichtet , um eine gemeinsame Bibliothek sowie Code für jeden meiner Bots bereitzustellen . Die aktuelle Verzeichnisstruktur sieht ungefähr so ​​aus:

lib/ 
  bot.land.d.ts
  common.ts
BlinkStalker/
  BlinkStalker.ts
  tsconfig.json
Artillery/
  Artillery.ts
  tsconfig.json
SmartMelee/
  SmartMelee.ts
  tsconfig.json

libist der allgemeine Code, der von Bots gemeinsam genutzt wird und TypeScript-Definitionen für die Bot Land-API (ohne TS) enthält. Jeder Bot erhält dann einen eigenen Ordner, wobei eine Datei den Bot-Code und die andere ein Boilerplate enthält tsconfig.json:

{
  "compilerOptions": {
    "target": "es3",
    "module": "none",
    "sourceMap": false,
    "outFile": "bot.js"
  },
  "files": [
    "MissileKite.ts"
  ],
  "include": [
    "../lib/**/*"
  ]
}

Wenn jeder erstellt tsconfig.jsonwird, wird ein entsprechender bot.jsCode erstellt, der den vom Bot selbst transpilierten Code sowie den gesamten Code enthält common.js. Dieses Setup ist unter anderem aus mehreren Gründen nicht optimal: Es erfordert viel doppeltes Boilerplate, erschwert das Hinzufügen neuer Bots, enthält viel unnötigen Code für jeden Bot und erfordert, dass jeder Bot separat erstellt wird.

Aufgrund meiner bisherigen Forschung scheint es jedoch nicht einfach zu sein, das zu tun, was ich will. Insbesondere die Verwendung der neuen tsc -bOption und der neuen Referenzen funktioniert nicht, da hierfür der Code modularisiert werden muss und für Bot Land eine einzige Datei mit allen auf der obersten Ebene definierten Funktionen erforderlich ist.

Was ist der beste Weg, um so viele der folgenden Ziele wie möglich zu erreichen?

  • Es ist keine neue Boilerplate erforderlich, um einen neuen Bot hinzuzufügen (z. B. keine tsconfig.jsonpro Bot).
  • Verwenden Sie diese Option importfür allgemeine Funktionen, um die Ausgabe von nicht verwendetem Code zu vermeiden.
  • Geben Sie weiterhin alle Funktionen als eine einzige Datei im spezifischen Format von Bot Land aus
  • Ein einzelner Erstellungsschritt, der mehrere Ausgabedateien erzeugt, eine für jeden Bot
  • Bonus: Integration des Build-Prozesses in VS Code. Für die Erstellung tasks.jsonjedes Teilprojekts gibt es derzeit eine entsprechende Kesselplatte .

Ich vermute vage, dass die Antwort wahrscheinlich zusätzlich etwas wie Grunzen beinhaltet tsc, aber ich weiß nicht genug darüber, um sicher zu sein.

Andrew Mao
quelle
Ist es notwendig, dass alle Bots separate Ordner haben? Oder reicht es aus, dass sich jeder Bot in einer einzelnen Datei auf der Stammebene befindet? (zB <root>/MissileKite.ts)
a1300
1
Müssen alle transpilierten Bot-Dateien benannt werden bot.js?
a1300
Root in einer einzelnen Datei wäre vorzuziehen; Sie befinden sich aufgrund der separaten Ordner in separaten Ordnern tsconfig.json. Transpilierte Bot-Dateien können beliebig benannt werden, vorzugsweise die .js-Version der Originaldatei. Ich habe es jetzt so in der Repo-Ausgabe an eingerichtet build/MissileKite.js.
Andrew Mao
1
@ andrew-mao Sie können einen Blick auf meine Vorlage für GAS-Projekte werfen, die die meisten Ihrer Anforderungen erfüllt (aber auf eine andere Umgebung abzielt). Wenn es Ihnen passt, kann ich sie möglicherweise nächste Woche für Sie anpassen. github.com/PopGoesTheWza/ts-gas-project-starter
PopGoesTheWza
Ist tsconfig-gas.jsondas Relevante dort zu sehen?
Andrew Mao

Antworten:

2

Hier ist mein Versuch , Ihre Anforderungen zu beantworten.

Bemerkenswerte Dateien:

  • src/tsconfig-botland.jsonEnthält Einstellungen für jedes bot.land- Skript (einschließlich Ihrer benutzerdefinierten Deklarationen, in die ich verschoben habe types/bot-land/index.d.ts). Sie können die strictEinstellungen ändern, die ich verwendet habe.
  • src/tsconfig.jsonenthält Verweise auf alle Ihre Bots. Dies ist die Datei, die bearbeitet werden muss, wenn Sie ein weiteres Bot-Skript hinzufügen möchten

Ein Bot-Skript besteht aus mindestens zwei Dateien: einer minimalistischen tsconfig.jsonund einer oder mehreren .tsSkriptdateien.

Zum Beispiel src/AggroMiner/tsconfig.json:

{
    "extends": "../tsconfig-botland",
    "compilerOptions": {
        "outFile": "../../build/AggroMiner.js"
    },
    "files": ["index.ts"],
    "include": ["**/*.ts", "../lib/**/*.ts"]
}

In den meisten Fällen sollten Sie Folgendes tun, um ein neues Bot-Skript zu starten:

  1. Kopieren Sie einen beliebigen Bot-Ordner (dh src/AggroMiner) in einen neuen Ordner untersrc
  2. Bearbeiten Sie die src/<newBotFolder>/tsconfig.json , um das outFilemit dem Namen Ihres Bots zu bearbeiten
  3. bearbeiten src/tsconfig.json und einen Verweis auf hinzufügensrc/<newBotFolder>

Das folgende npm/yarn Skript wurde festgelegt:

  • build um alle Bots zu bauen
  • build-cleandie den buildOrdner löschen, bevor abuild
  • formatum Prettier für alle .tsDateien unter auszuführensrc
  • lint um eine tslint-Prüfung für alle Bot-Skripte durchzuführen

Lassen Sie jetzt Ihre Anforderungen herunter:

  • Kein neues Boilerplate erforderlich, um einen neuen Bot hinzuzufügen (z. B. keine tsconfig.json pro Bot)

Um dies zu erreichen, müssten Sie ein Skript erstellen, das Ihren Bots-Ordner / Ihre Bots-Skripte auflistet ... und die entsprechenden pro Bot einrichten tsconfig.jsonund ausführen tsc. Sofern dies nicht unbedingt erforderlich ist, kann eine minimale Einrichtung (siehe oben) ausreichend sein.

  • Verwenden Sie den Import für allgemeine Funktionen, um die Ausgabe von nicht verwendetem Code zu vermeiden, aber dann ...

Beachten Sie zunächst , dass Sie, wenn Sie ein Modul export/ eine importAnweisung verwenden, zusätzliche Drittanbieter zum Packen / Treeshake benötigen, um eine einzelne Dateiausgabe zu erzielen. Soweit ich von Bot.land erfahren habe, werden Ihre Skripte auf dem Server ausgeführt. Wenn Deadcode keinen Einfluss auf Ihre Bot-Leistung hat, würde ich mich nicht wirklich darum kümmern.

  • Geben Sie weiterhin alle Funktionen als eine einzige Datei im spezifischen Format von Bot Land aus

Erledigt.

  • Ein einzelner Erstellungsschritt, der mehrere Ausgabedateien erzeugt, eine für jeden Bot

Erledigt.

  • Bonus: Integration des Build-Prozesses in VS Code. Für die Erstellung jedes Teilprojekts gibt es derzeit eine entsprechende Boilerplate-Task.json.

Die npmSkripte sollten in der Aufgabenliste von vsc erscheinen (zumindest in meiner), wodurch das tasks.jsonUnnötige unnötig wird.

PopGoesTheWza
quelle
Deadcode ist ein guter Kompromiss für alles andere, was Sie hier gemacht haben. Können Sie mir jedoch mitteilen, warum Sie types/bot-landdie Definitionen verwendet haben und warum Sie strictEinstellungen gewählt haben?
Andrew Mao
Types / bot-land / index.d.ts ist wirklich Ihre ursprüngliche .d.ts aus lib, umbenannt und anders platziert. Î Nehmen wir an, es beschreibt den allgemeinen Ausführungskontext von bot.land für alle Skripte und daher stelle ich sicher, dass er in jedem Bot-Skript immer verfügbar ist. Die 'strengen' Einstellungen sind nur hier, weil ich meine bevorzugten Einstellungen träge kopiert habe (dasselbe gilt für die hübscheren Einstellungen). Diese sollten an die Vorlieben des Benutzers (Sie) angepasst werden.
PopGoesTheWza
Ich frage mich nur, ob es einen üblichen Grund gibt, dies zu tun, typesoder ob dies nur eine bestimmte Art der Organisation war, die Sie ausgewählt haben.
Andrew Mao
Der einzige Grund ist die Annahme, dass es sich um den bot.land-Kontext handelt. Stellen Sie sich vor, Sie hätten die @ -Typen / Knoten-Typisierungen bereits in Ihren NodeJS-Skripten verfügbar
PopGoesTheWza
1
Ein / types-Ordner ist einer der herkömmlichen Orte, an denen externe Typdeklarationen abgelegt werden (dh ein spezifischer Ausführungskontext wie die Botland-Engine oder untypisierte JavaScript-Module / -Pakete, die hier nicht verwendet werden)
PopGoesTheWza
3

Sie könnten tatsächlich Projektreferenzen verwenden. Befolgen Sie diese Schritte, um die gleichen Ergebnisse zu erzielen, die Sie für Ihre Originaldateien erhalten haben, wobei sich alle Funktionen auf der obersten Ebene in einer Datei befinden. Ich konnte jedoch keine Lösung finden, um nur benötigte Funktionen in Bots zu importieren. Das heißt, ohne Importe und Exporte zu verwenden.

In Ihrer tsconfig.json im Stammverzeichnis

{
    "files": [],
    "references": [
        { "path": "./lib" }
        { "path": "./AggroMiner" }
        { "path": "./ArtilleryMicro" }
        { "path": "./MissileKite" }
        { "path": "./SmartMelee" }
        { "path": "./ZapKite" }
    ]
}

Fügen Sie als Nächstes in Ihrem lib-Ordner eine solche tsconfig.json hinzu

{
  "compilerOptions": {
    "declaration": true,
    "declarationMap": true,
    "composite": true,
    "rootDir": ".",
    "outFile": "../build/lib.js",
    "target": "es3",
    "removeComments": true,
    "sourceMap": false,
  },
  "files": [
    "data.ts",
    "movement.ts",
    "utils.ts"
  ]
}

Wir müssen einige Anpassungen in data.ts, motion.ts und utils.ts vornehmen, damit ts uns nicht mit Kompilierungsfehlern stört.

data.ts

/// <reference path="./bot.land.d.ts"/>

(...)

Bewegung.ts


/// <reference path="./data.ts"/>
/// <reference path="./utils.ts"/>
(...)

utils.ts

/// <reference path="./bot.land.d.ts"/>
(...)

Als nächstes fügen wir base.json im Stammverzeichnis hinzu (die Datei tsconfig.json der Bots erweitert sie).

base.json

{
  "compilerOptions": {
    "declaration": true,
    "composite": true,
    "rootDir": ".",
    "target": "es3",
    "removeComments": true,
    "sourceMap": false,
  }
}

und die tsconfig.json der Bots (entsprechend den Bots anpassen)

{
  "extends": "../base",
  "compilerOptions": {
    "outFile": "../build/AggroMiner.js",
  },
  "files": [
    "AggroMiner.ts"
  ],
  "references": [
      { "path": "../lib", "prepend": true } //note the prepend: true
  ]
}

Das ist es. Jetzt lauf einfach

tsc -b
jperl
quelle
Ich habe mir so etwas überlegt, aber der Grund, warum es nicht funktioniert, ist, dass die Datei, die in Ihrem Zweig ausgegeben wird, oben eine Menge solcher Dinge enthält und das Spiel eine Datei mit allen darin enthaltenen Funktionen benötigt. Dann müsste ich alle kompilierten Ausgaben manuell zusammenschustern, um die Datei zu erstellen, die ich hochladen würde, anstatt nur das Einfügen der Datei zu kopieren. `` use strict "; exportiert .__ esModule = true; var data_1 = require ("../ lib / data"); var motion_1 = require ("../ lib / motion"); var utils_1 = require ("../ lib / utils"); `
Andrew Mao
Aber es funktioniert, da lib auch im Build-Ordner ausgegeben (gebaut) wird (dank der Referenzen).
Jperl
Ich war gerade dabei, meinen Kommentar zu bearbeiten - siehe oben. Oder werfen Sie einen Blick auf das build/MissileKite.js, was ausgegeben wird, wenn Sie das ursprüngliche Repo erstellen.
Andrew Mao
@AndrewMao Entschuldigung, erst jetzt verstehe ich, was Sie mit "weil der Code modularisiert werden muss und Bot Land eine einzelne Datei mit allen auf der obersten Ebene definierten Funktionen erfordert" gemeint haben. Ich habe über die Verwendung von "prepend: true" nachgedacht, aber dafür muss outFile verwendet werden, und ts lässt uns die Dateien nicht in lib kompilieren, da einige von anderen abhängig sind.
Jperl
@ AndrewMao Ich habe Webpack-Unterstützung hinzugefügt. Ich habe den Beitrag bearbeitet und die Änderungen auf das Repo übertragen. Lass es mich wissen, wenn es besser ist.
Jperl