Configurer un projet TypeScript avec des dépendances communes pour créer plusieurs fichiers de sortie JavaScript simples

10

J'écris actuellement des scripts pour Bot Land . Bot Land est un jeu de stratégie en temps réel où au lieu de contrôler vos unités avec une souris et un clavier, vous écrivez du code pour contrôler vos bots via une API, puis vos bots vont combattre les bots des autres. Si vous connaissez les unités de SC2, vous pouvez créer des robots similaires aux harceleurs clignotants, aux chars de siège, aux médecins et aux ultralisks. (C'est un jeu assez amusant pour les ingénieurs logiciels, mais cela sort du cadre de cette question.)

bot land

Le contrôle des bots a trois niveaux de complexité croissante: une IA par défaut, un langage de programmation de type Scratch et un ensemble réduit de JavaScript appelé BotLandScript. Bien que l'éditeur intégré pour BotLandScript soit raisonnable, vous devez télécharger tout votre code en un seul fichier avec des fonctions globales de premier niveau partout. Naturellement, cela commence à devenir douloureux après un certain temps si votre code commence à devenir long et que des robots différents partagent les mêmes fonctions.

environnement de programmation

Pour faciliter l'écriture de code pour plusieurs robots, réduire le risque d'erreurs involontaires lors du codage en JS nu et augmenter mes chances de battre les autres joueurs, j'ai configuré le projet TypeScript ci - dessus pour fournir une bibliothèque commune ainsi que du code pour chacun de mes robots . La structure de répertoire actuelle ressemble approximativement à la suivante:

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

libest le code commun qui est partagé entre les bots et fournit des définitions TypeScript pour l'API Bot Land (non-TS). Chaque bot obtient alors son propre dossier, avec un fichier contenant le code du bot et l'autre un passe-partout tsconfig.json:

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

Quand chacun tsconfig.jsonest construit, il crée un correspondant bot.jsqui contient le code transpilé du bot lui-même ainsi que tout le code common.js. Cette configuration est sous-optimale pour plusieurs raisons, entre autres: elle nécessite beaucoup de passe-partout en double, rend difficile l'ajout de nouveaux bots, inclut beaucoup de code inutile pour chaque bot et nécessite que chaque bot soit construit séparément.

Cependant, d'après mes recherches jusqu'à présent , il ne semble pas qu'il existe un moyen facile de faire ce que je veux. En particulier, l'utilisation de la nouvelle tsc -boption et des nouvelles références ne fonctionne pas, car cela nécessite une modularisation du code et Bot Land nécessite un fichier unique avec toutes les fonctions définies au niveau supérieur.

Quelle est la meilleure façon d'obtenir le plus grand nombre possible des éléments suivants?

  • Aucun nouveau passe-partout requis pour ajouter un nouveau bot (par exemple, aucun tsconfig.jsonpar bot)
  • Utilisez importpour les fonctions courantes pour éviter de sortir du code inutilisé, mais alors ...
  • Toujours afficher toutes les fonctions dans un seul fichier au format spécifique de Bot Land
  • Une seule étape de génération qui produit plusieurs fichiers de sortie, un pour chaque bot
  • Bonus: intégration du processus de construction avec VS Code. Il existe actuellement un passe-partout correspondant tasks.jsonpour la construction de chaque sous-projet.

Je suppose vaguement que la réponse implique probablement quelque chose comme Grunt en plus tsc, mais je n'en sais pas assez pour en être sûr.

Andrew Mao
la source
Est-il nécessaire que tous les robots aient des dossiers séparés? Ou suffit-il que chaque bot soit au niveau racine dans un seul fichier? (par exemple <root>/MissileKite.ts)
a1300
1
Tous les fichiers bots transpilés doivent-ils être nommés bot.js?
a1300
La racine dans un seul fichier serait préférable; ils sont dans des dossiers séparés en raison de la séparation tsconfig.json. Les fichiers bots transpilés peuvent être nommés n'importe quoi, de préférence la version .js du fichier d'origine. Je l'ai configuré de cette façon maintenant dans le repo en sortie vers build/MissileKite.js.
Andrew Mao
1
@ andrew-mao Vous pouvez jeter un œil à mon modèle de projets GAS qui répond à la plupart de vos besoins (mais en ciblant un environnement différent) Si cela vous convient, je pourrai peut-être l'adapter pour vous dans le courant de la semaine prochaine. github.com/PopGoesTheWza/ts-gas-project-starter
PopGoesTheWza
Est-ce tsconfig-gas.jsonla chose pertinente à regarder là-bas?
Andrew Mao

Réponses:

2

Voici ma tentative de répondre à vos besoins.

Fichiers notables:

  • src/tsconfig-botland.jsoncontient les paramètres de tout script bot.land (y compris vos déclarations personnalisées vers lesquelles je suis passé types/bot-land/index.d.ts). Vous pouvez modifier les strictparamètres que j'ai utilisés.
  • src/tsconfig.jsoncontient des références à tous vos robots. Il s'agit du fichier à modifier chaque fois que vous souhaitez ajouter un autre script de bot

Un script de bot est composé d'au moins deux fichiers: un minimaliste tsconfig.jsonet un ou plusieurs .tsfichiers de script.

Par exemple src/AggroMiner/tsconfig.json:

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

Dans la plupart des cas, pour démarrer un nouveau script de bot, vous devez:

  1. copier n'importe quel dossier de bot (ie src/AggroMiner) dans un nouveau dossier soussrc
  2. modifier le src/<newBotFolder>/tsconfig.jsonpour modifier le outFileavec le nom de votre bot
  3. modifier src/tsconfig.jsonet ajouter une référence àsrc/<newBotFolder>

Le script npm/ suivant yarna été défini:

  • build pour construire tous les bots
  • build-cleanqui efface le builddossier avant d'exécuter unbuild
  • formatpour exécuter Prettier sur tous les .tsfichiers soussrc
  • lint exécuter une vérification tslint sur tous les scripts de bot

Exécuter maintenant vos exigences:

  • Aucun nouveau passe-partout requis pour ajouter un nouveau bot (par exemple pas de tsconfig.json par bot)

Pour y parvenir, il faudrait créer un script qui énumérerait votre dossier / scripts de bots ... et configurer le bot approprié tsconfig.jsonet l'exécuter tsc. À moins que cela ne soit strictement nécessaire, une configuration minimale (décrite ci-dessus) peut être suffisante.

  • Utilisez l'importation pour les fonctions courantes pour éviter de sortir du code inutilisé, mais alors ...

Tout d'abord, sachez que si vous commencez à utiliser des modules export/ importinstructions, vous aurez besoin de tiers supplémentaires pour emballer / arborescence afin d'obtenir une sortie de fichier unique. D'après ce que j'ai pu recueillir sur Bot.land, vos scripts s'exécutent sur le serveur. À moins que le code mort n'ait un impact sur les performances de votre bot, je ne me dérangerais pas vraiment.

  • Toujours afficher toutes les fonctions dans un seul fichier au format spécifique de Bot Land

Terminé.

  • Une seule étape de génération qui produit plusieurs fichiers de sortie, un pour chaque bot

Terminé.

  • Bonus: intégration du processus de construction avec VS Code. Il existe actuellement un fichier de tâches correspondant pour la construction de chaque sous-projet.

Les npmscripts devraient apparaître dans la liste des tâches de vsc (du moins ils le font dans la mienne), ce qui rend tasks.jsoninutile.

PopGoesTheWza
la source
Deadcode est un bon compromis pour tout ce que vous avez fait ici; pouvez-vous me dire pourquoi vous avez utilisé types/bot-landles définitions et pourquoi vous avez choisi les strictparamètres?
Andrew Mao
Types / bot-land / index.d.ts est vraiment votre .d.ts d'origine de lib, renommé et placé différemment. Je suppose qu'il décrit en quelque sorte le contexte général d'exécution de bot.land pour tous les scripts et donc je m'assure qu'il est toujours disponible dans chaque script de bot. Les paramètres «stricts» ne sont là que parce que j'ai copié paresseusement mes paramètres préférés (de même pour les paramètres les plus jolis). Ceux-ci doivent être adaptés à la préférence de l'utilisateur (vous).
PopGoesTheWza
Je me demande simplement s'il y a une raison habituelle de mettre cela typesou si c'était juste une façon spécifique d'organiser ce que vous avez choisi.
Andrew Mao
La seule raison est que, en supposant que c'était le contexte bot.land. Pensez-y comme avoir les typages @ types / node déjà disponibles dans vos scripts nodejs
PopGoesTheWza
1
Un dossier / types est l'un des endroits conventionnels où l'on met des déclarations de types externes (c'est-à-dire un contexte d'exécution spécifique comme le moteur botland ou des modules / packages JavaScript non typés, qui ne sont pas utilisés ici)
PopGoesTheWza
3

Vous pouvez réellement utiliser des références de projet. Suivez ces étapes pour obtenir les mêmes résultats que vous obteniez pour vos fichiers d'origine, avec toutes les fonctions au niveau supérieur dans un seul fichier. Cependant, je n'ai pas trouvé de solution pour importer uniquement les fonctions nécessaires dans les bots. Autrement dit, sans utiliser les importations et les exportations.

Dans votre tsconfig.json à la racine

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

Ensuite, dans votre dossier lib, ajoutez un tsconfig.json comme ça

{
  "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"
  ]
}

Nous devons faire quelques ajustements dans data.ts, movement.ts et utils.ts afin que ts ne nous dérange pas avec des erreurs de compilation.

data.ts

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

(...)

mouvement.ts


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

utils.ts

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

Ensuite, nous ajoutons base.json à la racine (le tsconfig.json des bots va l'étendre).

base.json

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

et tsconfig.json des bots (adapter en fonction des bots)

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

C'est ça. Maintenant lancez

tsc -b
jperl
la source
J'ai donc pensé à quelque chose comme ça, mais la raison pour laquelle cela ne fonctionne pas, c'est parce que le fichier qui est sorti sur votre branche a un tas de choses comme ça en haut, et le jeu a besoin d'un fichier avec toutes les fonctions qu'il contient. Il me faudrait donc bricoler manuellement toutes les sorties compilées ensemble pour créer le fichier que je téléchargerais, au lieu de simplement copier-coller le fichier. `" utiliser strictement "; exporte .__ esModule = true; var data_1 = require ("../ lib / data"); var movement_1 = require ("../ lib / movement"); var utils_1 = require ("../ lib / utils"); `
Andrew Mao du
Mais cela fonctionne puisque lib est également sorti (construit) dans le dossier de construction (grâce aux références).
jperl
J'étais en train de modifier mon commentaire - voir ci-dessus. Ou jetez un œil à la build/MissileKite.jssortie lorsque vous créez le référentiel d'origine.
Andrew Mao
@AndrewMao désolé, seulement maintenant je comprends ce que vous entendiez par "car cela nécessite que le code soit modularisé et Bot Land nécessite un seul fichier avec toutes les fonctions définies au niveau supérieur.". J'ai pensé à utiliser "prepend: true" mais cela nécessite d'utiliser outFile et ts ne nous permettra pas de compiler les fichiers en lib car certains dépendent d'autres.
jperl
@AndrewMao J'ai ajouté le support Webpack. J'ai édité le post et j'ai poussé les changements sur le repo. Faites-moi savoir si c'est mieux.
jperl