2016-06-26 1 views
-1

Modifier: Pour plus de clarté, je suis à la recherche d'un transtypeur de refactoring JavaScript ou TypeScript. Je veux faire le renommer à construire fois, pas au moment de l'exécution. Je souhaite utiliser une bibliothèque particulière, mais son API ne correspond pas au style de nommage de mon propre code (familier?), Donc je veux remplacer les appels API avec des appels identiques qui utilisent les conventions de codage de mon propre code.Refactorisé par script pour Javascript ou y a-t-il un transcripteur de refactoring pour JavaScript?

Mais cela introduit beaucoup de temps d'exécution, avec plus d'une centaine de méthodes introduites dans les prototypes pour changer la signature des objets.

Object.defineProperty(Room.prototype, 'getController', {get: function() {return this.controller}}); 

Au lieu de cela, si je pouvais introduire une étape de compilation qui a exécuté les règles de refactoring sur les appels API, mon code pourrait nous mon API préférée, mais le code de résultat pourrait utiliser l'API publiée. Je ne l'ai pas été en mesure de trouver un outil de refactoring JavaScript qui a un DSL pour l'exécution des demandes de refactoring répétitives, quelque chose comme:

rename(Room.prototype.controller, 'getController') 

Recherche de « refactoring automatique » apporte des dizaines de projets pie-in-the-ciel pour rendre le code meilleur sans aucune intervention humaine. Ce n'est pas ce que je veux; Je veux juste introduire le refactoring scripté manuellement entré dans mon pipeline de construction pour protéger le code de travail des différentes conventions de nommage des API externes.

+1

Enveloppez tout votre code dans un IIFE et isolez-le de l'espace de noms global ??Pas tout à fait clair où les collisions se produisent – charlietfl

+0

C'est exactement ce que je fais en ce moment, mais les appels de fonctions supplémentaires sont des frais généraux que je veux éviter, d'où ma demande pour un moyen de renommer-sur-transpiler. –

Répondre

0

Pas certain si js à l'approche renvoie les résultats attendus, mais devrait être en mesure d'ajuster le nom, définir le contexte de la fonction en créant un objet séparé, en utilisant Function.prototype.bind(). La mise en œuvre pourrait probablement être améliorée pour répondre aux besoins exacts.

function Mirror() { 
 
    var ctx = Object.create(null); 
 
    this.rename = function(obj, name, context, args) { 
 
    ctx[name] = obj.bind(context || ctx, args || null); 
 
    return ctx 
 
    } 
 
} 
 

 
// `Room` 
 
function Room() { 
 

 
} 
 

 
Room.prototype.controller = function() { 
 
    console.log(this) 
 
} 
 

 
var mirror = new Mirror(); 
 
let _room = mirror.rename(
 
       Room.prototype.controller 
 
       , "getController" 
 
       , new Room()); 
 
_room.getController()

Une autre approche pourrait être de modifier les noms de fonction un à la fois à l'éditeur de texte.

+0

Peut-être que je suis un malentendu, mais cela semble être un DSL pour rendre le Object.defineProperty() renommer à l'exécution. Mon objectif est que le code déployé utilise directement l'API publiée, mais que le code interne utilise une API privée, avec une étape de changement de nom. –

+0

@MyrddinEmrys _ "Mon objectif est que le code déployé utilise directement l'API publiée, mais que le code interne utilise une API privée, avec une étape de changement de nom sur transpile" _ Voir l'approche alternative. L'effort placé dans la création d'un tel processus pourrait être utilisé pour réécrire toute l'API existante. – guest271314

+0

Je n'ai pas accès à réécrire l'API existante. Ou je le ferais. Je sais que c'est JavaScript, mais dans ce cas, l'API n'est pas disponible pour modification. –

1

Ce que vous voulez est une source à la source program transformation system (PTS).

Ces outils analysent le code source dans RSHS, vous offrent la possibilité de transformer ces RSHS dans d'autres RSHS avec des motifs de surface syntaxe, puis cracher sur la source code pour l'AST révisé en conséquence. Si vous passez des AST dans une langue aux AST dans une autre, vous obtenez ce que vous appelez un transpiler (ce n'est pas mon terme préféré, PTS était très bien).

Source à la syntaxe de transformation source varie, mais en substance, vous écrivez quelque chose comme cela en utilisant la surface syntaxe du langage (réelle) des langues source et cible:

if you see *this*, replace it by *that* if some_condition(*this*) 

Le ce et cet élément sont des motifs exprimés dans la syntaxe du langage; Le conditionnel optionnel ("contrainte sémantique") permet à l'outil de prendre en compte les informations de contexte (telles que les propriétés des symboles, etc.). Souvent, il faut plusieurs ou plusieurs règles pour accomplir une transformation complexe; les règles peuvent généralement être séquencées pour obtenir un effet composé d'intérêt. L'expression des rulles et leur séquencement sont des "scripts" qui intéressent OP.

Vous devez soit en obtenir un qui acceptera les nouvelles définitions de langue (les très générales seront) et définissez JavaScript, ou en obtenir un qui a déjà un analyseur JavaScript disponible et simplement l'utiliser. Un de ces genres, PTS est notre DMS Software Reengineering Toolkit. Il a un Javascript front end disponible.

problème de changement de nom de OP peut être spécifié comme en a DMS Rewrite Rule comme suit:

rule rename_controller(): IDENTIFIER 
    "controller" -> "getController"; 

Les guillemets sont méta citations utilisées pour distinguer la syntaxe de la langue de motif, de la syntaxe des langauges source/cible , qui est écrit à l'intérieur des méta quotes. Pour renommer tous les appels API, OP aurait besoin de règles pour chaque entrée d'API unique.

Bien que facile à écrire, c'est plutôt lourd; tout identifiant portant ce nom dans les fichiers traités par le sera renommé ainsi. Cela peut renommer "controller" dans les portées que OP n'a pas l'intention. S'il n'y a pas de risque de plusieurs déclarations avec le même nom, alors il sera possible de l'exécuter en toute sécurité en utilisant la tactique intégrée de DMS «appliquer les règles partout» comme tâche scriptable en ligne de commande. (Il peut être plus facile de renommer les identifiants utilisés dans le code OP pour les API, il est plus facile d'éviter les collisions de noms dans le code qu'il écrit que les collisions de noms dans les fichiers sources des API qu'il n'a pas écrites).

Pour ce faire droit, OP doit se qualifier en quelque sorte qui déclaration de « contrôleur » il veut renommé. Ce qu'il devra définir une contrainte personnalisée qui vérifie qu'il est renommant le « bon », quelque chose comme:

rule rename_controller(i:IDENTIFIER): IDENTIFIER 
    "\i" -> "getController" 
      if (match(i,"controller") & declaration_at_line(i,1219); 

nous nous adaptons ici un identifiant avec l'intention d'obtenir un accès au nœud AST dans lequel il est trouvé (\ texte de syntaxe de surface interne, i en dehors de ce texte.). Le prédicat match est déjà intégré dans DMS; il est utilisé pour insister sur le fait que nous avons trouvé le bon nom. Le prédicat declaration_at_line est utilisé pour vérifier que le nœud AST correspondant correspond à un nœud ayant le même nom déclaré à un emplacement spécifique dans le fichier. Cela nécessite ce qui équivaut à une recherche de nom étendue qui suit les règles ECMAScript, donc un travail personnalisé est requis. (À l'heure actuelle, ce front-end DMS ne fournit pas cela, d'autres frontaux DMS pour d'autres langues fournissent parfois cette capacité hors de la boîte). Le choix de la manière de contraindre quelle déclaration est la cible est plutôt arbitraire; on pourrait définir par le chemin de la racine de l'espace de noms global pour arriver à atteindre comme OP laisse deviner dans son exemple:

 ... declaration_in(i,"Room.prototype") ... 

On aura toujours besoin de règles de consultation de portée pour traiter un tel chemin.

Nous remarquons que quel que soit le PTS choisi par OP, une recherche de portée sera nécessaire pour un renommage précis. Beaucoup d'autres PTS (TXL, Stratego, ...) n'apportent aucun support pour la mise en œuvre de ce système.

+0

Je connaissais le terme AST et je cherchais des façons de le faire moi-même à partir de la sortie AST d'un outil. Je n'avais pas entendu parler du terme PTS, alors merci pour cela (un autre outil pour Google). Une question rapide de capacité (puisque votre documentation n'est pas publique autant que je peux trouver): puis-je contraindre une déclaration à un fichier plutôt qu'à une ligne? –

+0

Vous ne pouvez pas faire cela avec "juste" un AST; quelque chose doit être capable de régénérer le code * valide * de l'AST. C'est plus difficile que ça en a l'air (voir http://stackoverflow.com/a/5834775/120163) Ceci est juste une des nombreuses raisons pour lesquelles un pur parser n'est vraiment pas suffisant (voir "Life After Parsing" http: //www.semdesigns .com/Produits/DMS/LifeAfterParsing.html pour beaucoup plus de détails) –

+0

En ce qui concerne les contraintes: DMS est un PTS personnalisable .; il doit être de cette façon pour répondre à l'ensemble très varié de choses que les gens veulent faire pour le code source. Vous pouvez configurer pour lire autant ou aussi peu de fichiers source d'intérêt; il est clair que tout fichier qu'il ne lit pas ne sera pas modifié: -} Vous pouvez implémenter une telle contrainte de restriction de fichier assez facilement (chaque noeud AST est marqué comme étant le fichier source d'où il provient). Le prédicat semanitc doit inspecter le noeud AST pour l'indication du fichier et vérifier que le fichier est le "un" d'intérêt. –