flow-remove-types
Advanced tools
Comparing version 1.1.2 to 1.2.0
180
index.js
@@ -72,4 +72,11 @@ var babylon = require('babylon'); | ||
visit(ast, removedNodes, removeFlowVisitor); | ||
var context = { | ||
ast: ast, | ||
source: source, | ||
removedNodes: removedNodes, | ||
pretty: Boolean(options && options.pretty) | ||
}; | ||
visit(ast, context, removeFlowVisitor); | ||
return resultPrinter(options, source, removedNodes); | ||
@@ -143,3 +150,3 @@ } | ||
Identifier: function (removedNodes, node, ast) { | ||
Identifier: function (context, node, ast) { | ||
if (node.optional) { | ||
@@ -151,22 +158,39 @@ // Find the optional token. | ||
} while (ast.tokens[idx].type.label !== '?'); | ||
removeNode(removedNodes, ast.tokens[idx]); | ||
removeNode(context, ast.tokens[idx]); | ||
} | ||
}, | ||
ClassProperty: function (removedNodes, node) { | ||
ClassProperty: function (context, node) { | ||
if (!node.value) { | ||
return removeNode(removedNodes, node) | ||
return removeNode(context, node) | ||
} | ||
}, | ||
ExportNamedDeclaration: function (removedNodes, node) { | ||
if (node.exportKind === 'type') { | ||
return removeNode(removedNodes, node); | ||
ExportNamedDeclaration: function (context, node) { | ||
if (node.exportKind === 'type' || node.exportKind === 'typeof') { | ||
return removeNode(context, node); | ||
} | ||
}, | ||
ImportDeclaration: function (removedNodes, node) { | ||
if (node.importKind === 'type') { | ||
return removeNode(removedNodes, node); | ||
ImportDeclaration: function (context, node) { | ||
if (node.importKind === 'type' || node.importKind === 'typeof') { | ||
return removeNode(context, node); | ||
} | ||
}, | ||
ImportSpecifier: function(context, node) { | ||
if (node.importKind === 'type' || node.importKind === 'typeof') { | ||
removeNode(context, node); | ||
// Remove trailing comma | ||
var ast = context.ast; | ||
var idx = findTokenIndex(ast.tokens, node.end); | ||
while (isComment(ast.tokens[idx])) { | ||
idx++; | ||
} | ||
if (ast.tokens[idx].type.label === ',') { | ||
removeNode(context, ast.tokens[idx]); | ||
} | ||
return false; | ||
} | ||
} | ||
@@ -177,3 +201,3 @@ }; | ||
// the associated tokens. | ||
function removeImplementedInterfaces(removedNodes, node, ast) { | ||
function removeImplementedInterfaces(context, node, ast) { | ||
if (node.implements && node.implements.length > 0) { | ||
@@ -189,5 +213,4 @@ var first = node.implements[0]; | ||
do { | ||
if (ast.tokens[idx].type !== 'CommentBlock' && | ||
ast.tokens[idx].type !== 'CommentLine') { | ||
removeNode(removedNodes, ast.tokens[idx]); | ||
if (!isComment(ast.tokens[idx])) { | ||
removeNode(context, ast.tokens[idx]); | ||
} | ||
@@ -200,16 +223,130 @@ } while (idx++ !== lastIdx); | ||
// in the list. | ||
function removeNode(removedNodes, node) { | ||
function removeNode(context, node) { | ||
var removedNodes = context.removedNodes; | ||
var length = removedNodes.length; | ||
var index = length; | ||
// Check for line's leading and trailing space to be removed. | ||
var spaceNode = context.pretty ? getLeadingSpaceNode(context, node) : null; | ||
var lineNode = context.pretty ? getTrailingLineNode(context, node) : null; | ||
while (index > 0 && removedNodes[index - 1].end > node.start) { | ||
index--; | ||
} | ||
if (index === length) { | ||
if (spaceNode) { | ||
removedNodes.push(spaceNode); | ||
} | ||
removedNodes.push(node); | ||
if (lineNode) { | ||
removedNodes.push(lineNode); | ||
} | ||
} else { | ||
removedNodes.splice(index, 0, node); | ||
if (lineNode) { | ||
if (spaceNode) { | ||
removedNodes.splice(index, 0, spaceNode, node, lineNode); | ||
} else { | ||
removedNodes.splice(index, 0, node, lineNode); | ||
} | ||
} else if (spaceNode) { | ||
removedNodes.splice(index, 0, spaceNode, node); | ||
} else { | ||
removedNodes.splice(index, 0, node); | ||
} | ||
} | ||
return false; | ||
} | ||
function getLeadingSpaceNode(context, node) { | ||
var source = context.source; | ||
var end = node.start; | ||
var start = end; | ||
while (source[start - 1] === ' ' || source[start - 1] === '\t') { | ||
start--; | ||
} | ||
if (start !== end) { | ||
return { | ||
start: start, | ||
end: end, | ||
loc: { start: node.loc.start, end: node.loc.start } | ||
}; | ||
} | ||
} | ||
function getTrailingLineNode(context, node) { | ||
var source = context.source; | ||
var start = node.end; | ||
var end = start; | ||
while (source[end] === ' ' || source[end] === '\t') { | ||
end++; | ||
} | ||
// Remove all space including the line break if this token was alone on the line. | ||
if (source[end] === '\n' || source[end] === '\r') { | ||
if (source[end] === '\r' && source[end + 1] === '\n') { | ||
end++; | ||
} | ||
end++; | ||
if (isLastNodeRemovedFromLine(context, node)) { | ||
return { | ||
start: start, | ||
end: end, | ||
loc: { start: node.loc.end, end: node.loc.end } | ||
}; | ||
} | ||
} | ||
} | ||
// Returns true if node is the last to be removed from a line. | ||
function isLastNodeRemovedFromLine(context, node) { | ||
var tokens = context.ast.tokens; | ||
var priorTokenIdx = findTokenIndex(tokens, node.start) - 1; | ||
var token = tokens[priorTokenIdx]; | ||
var line = node.loc.end.line; | ||
// Find previous token that was not removed on the same line. | ||
while (priorTokenIdx >= 0 && | ||
token.loc.end.line === line && | ||
isRemovedToken(context, token)) { | ||
token = tokens[--priorTokenIdx] | ||
} | ||
// If there's no prior token (start of file), or the prior token is on another | ||
// line, this line must be fully removed. | ||
return !token || token.loc.end.line !== line; | ||
} | ||
// Returns true if the provided token was previously marked as removed. | ||
function isRemovedToken(context, token) { | ||
var removedNodes = context.removedNodes; | ||
var nodeIdx = removedNodes.length - 1; | ||
// Find the last removed node which could possibly contain this token. | ||
while (nodeIdx >= 0 && removedNodes[nodeIdx].start > token.start) { | ||
nodeIdx--; | ||
} | ||
var node = removedNodes[nodeIdx]; | ||
// This token couldn't be removed if not contained within the removed node. | ||
if (nodeIdx === -1 || node.end < token.end) { | ||
return false; | ||
} | ||
// Iterate through the tokens contained by the removed node to find a match. | ||
var tokens = context.ast.tokens; | ||
var tokenIdx = findTokenIndex(tokens, node.start); | ||
while (tokens[tokenIdx].end <= node.end) { | ||
if (token === tokens[tokenIdx]) { | ||
return true; | ||
} | ||
tokenIdx++; | ||
} | ||
return false; | ||
} | ||
// Given the AST output of babylon parse, walk through in a depth-first order, | ||
@@ -269,2 +406,7 @@ // calling methods on the given visitor, providing context as the first argument. | ||
// True if the provided token is a comment. | ||
function isComment(token) { | ||
return token.type === 'CommentBlock' || token.type === 'CommentLine'; | ||
} | ||
// Produce a string full of space characters of a given size. | ||
@@ -292,2 +434,6 @@ function space(size) { | ||
var mappings = ''; | ||
if (!removedNodes || removedNodes.length === '') { | ||
return mappings; | ||
} | ||
var end = { line: 1, column: 0 }; | ||
@@ -294,0 +440,0 @@ |
{ | ||
"name": "flow-remove-types", | ||
"version": "1.1.2", | ||
"version": "1.2.0", | ||
"description": "Removes Flow type annotations from JavaScript files with speed and simplicity.", | ||
@@ -20,9 +20,9 @@ "author": "Lee Byron <lee@leebyron.com> (http://leebyron.com/)", | ||
], | ||
"homepage": "https://github.com/leebyron/flow-remove-types", | ||
"homepage": "https://github.com/flowtype/flow-remove-types", | ||
"bugs": { | ||
"url": "https://github.com/leebyron/flow-remove-types/issues" | ||
"url": "https://github.com/flowtype/flow-remove-types/issues" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "http://github.com/leebyron/flow-remove-types.git" | ||
"url": "http://github.com/flowtype/flow-remove-types.git" | ||
}, | ||
@@ -42,5 +42,5 @@ "scripts": { | ||
"dependencies": { | ||
"babylon": "^6.8.4", | ||
"babylon": "^6.15.0", | ||
"vlq": "^0.2.1" | ||
} | ||
} |
@@ -5,3 +5,3 @@ flow-remove-types | ||
[![npm](https://img.shields.io/npm/v/flow-remove-types.svg?maxAge=86400)](https://www.npmjs.com/package/flow-remove-types) | ||
[![Build Status](https://img.shields.io/travis/leebyron/flow-remove-types.svg?style=flat&label=travis&branch=master)](https://travis-ci.org/leebyron/flow-remove-types) | ||
[![Build Status](https://img.shields.io/travis/flowtype/flow-remove-types.svg?style=flat&label=travis&branch=master)](https://travis-ci.org/flowtype/flow-remove-types) | ||
@@ -93,6 +93,2 @@ Turn your JavaScript with [Flow](https://flowtype.org/) type annotations into | ||
As always, don't forget to use `flow-remove-types` to compile files before | ||
distributing your code on npm, as using the require hook affects the whole | ||
runtime and not just your module. | ||
You can also provide options to the require hook: | ||
@@ -105,3 +101,3 @@ | ||
Use options to define exactly which files to `include` or `exclude` with regular | ||
Use options to define exactly which files to `includes` or `excludes` with regular | ||
expressions. All files are included by default except those found in the | ||
@@ -111,6 +107,11 @@ `node_modules` folder, which is excluded by default. | ||
```js | ||
require('flow-remove-types/register')({ include: /\/custom_path\// }) | ||
require('flow-remove-types/register')({ includes: /\/custom_path\// }) | ||
``` | ||
> #### Don't use the require hook in packages distributed on NPM | ||
> As always, don't forget to use `flow-remove-types` to compile files before distributing | ||
> your code on npm, as using the require hook affects the whole runtime and not | ||
> just your module and may hurt the runtime performance of code that includes it. | ||
## Dead-Simple Transforms | ||
@@ -117,0 +118,0 @@ |
@@ -6,5 +6,7 @@ var flowRemoveTypes = require('./index'); | ||
// - all: Transform all files, not just those with a @flow comment. | ||
// - includes: A Regexp to determine which files should be transformed. | ||
// - excludes: A Regexp to determine which files should not be transformed, | ||
// defaults to ignoring /node_modules/, set to null to excludes nothing. | ||
// - includes: A Regexp/String to determine which files should be transformed. | ||
// (alias: include) | ||
// - excludes: A Regexp/String to determine which files should not be | ||
// transformed, defaults to ignoring /node_modules/, provide null | ||
// to exclude nothing. (alias: exclude) | ||
var options; | ||
@@ -36,5 +38,31 @@ module.exports = function setOptions(newOptions) { | ||
function shouldTransform(filename, options) { | ||
var includes = options && options.includes; | ||
var excludes = options && 'excludes' in options ? options.excludes : /\/node_modules\//; | ||
return (!includes || include.test(filename)) && !(excludes && excludes.test(filename)); | ||
var includes = options && regexpPattern(options.includes || options.include); | ||
var excludes = | ||
options && 'excludes' in options ? regexpPattern(options.excludes) : | ||
options && 'exclude' in options ? regexpPattern(options.exclude) : | ||
/\/node_modules\//; | ||
return (!includes || includes.test(filename)) && !(excludes && excludes.test(filename)); | ||
} | ||
// Given a null | string | RegExp | any, returns null | Regexp or throws a | ||
// more helpful error. | ||
function regexpPattern(pattern) { | ||
if (!pattern) { | ||
return pattern; | ||
} | ||
// A very simplified glob transform which allows passing legible strings like | ||
// "myPath/*.js" instead of a harder to read RegExp like /\/myPath\/.*\.js/. | ||
if (typeof pattern === 'string') { | ||
pattern = pattern.replace(/\./g, '\\.').replace(/\*/g, '.*'); | ||
if (pattern[0] !== '/') { | ||
pattern = '/' + pattern; | ||
} | ||
return new RegExp(pattern); | ||
} | ||
if (typeof pattern.test === 'function') { | ||
return pattern; | ||
} | ||
throw new Error( | ||
'flow-remove-types: includes and excludes must be RegExp or path strings. Got: ' + pattern | ||
); | ||
} |
Sorry, the diff of this file is not supported yet
36681
462
240
Updatedbabylon@^6.15.0