flow-remove-types
Advanced tools
Comparing version 1.2.3 to 2.99.0
273
index.js
@@ -1,2 +0,9 @@ | ||
var babylon = require('babylon'); | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
var parse = require('flow-parser').parse; | ||
var vlq = require('vlq'); | ||
@@ -45,10 +52,12 @@ | ||
// Babylon is one of the sources of truth for Flow syntax. This parse | ||
// configuration is intended to be as permissive as possible. | ||
var ast = babylon.parse(source, { | ||
allowImportExportEverywhere: true, | ||
allowReturnOutsideFunction: true, | ||
allowSuperOutsideMethod: true, | ||
sourceType: 'module', | ||
plugins: [ '*', 'jsx', 'flow' ], | ||
// This parse configuration is intended to be as permissive as possible. | ||
var ast = parse(source, { | ||
esproposal_decorators: true, | ||
esproposal_class_instance_fields: true, | ||
esproposal_class_static_fields: true, | ||
esproposal_export_star_as: true, | ||
esproposal_optional_chaining: true, | ||
esproposal_nullish_coalescing: true, | ||
types: true, | ||
tokens: true, | ||
}); | ||
@@ -62,3 +71,3 @@ | ||
removedNodes: removedNodes, | ||
pretty: Boolean(options && options.pretty) | ||
pretty: Boolean(options && options.pretty), | ||
}; | ||
@@ -68,6 +77,9 @@ | ||
if (pragmaStart !== -1) { | ||
var pragmaIdx = findTokenIndex(ast.tokens, pragmaStart); | ||
var pragmaType = ast.tokens[pragmaIdx].type; | ||
if (pragmaType === 'CommentLine' || pragmaType === 'CommentBlock') { | ||
removedNodes.push(getPragmaNode(context, pragmaStart, pragmaSize)); | ||
var comments = getComments(ast); | ||
var pragmaIdx = findTokenIndex(comments, pragmaStart); | ||
if (pragmaIdx >= 0 && pragmaIdx < comments.length) { | ||
var pragmaType = comments[pragmaIdx].type; | ||
if (pragmaType === 'Line' || pragmaType === 'Block') { | ||
removedNodes.push(getPragmaNode(context, pragmaStart, pragmaSize)); | ||
} | ||
} | ||
@@ -80,3 +92,3 @@ } | ||
return resultPrinter(options, source, removedNodes); | ||
} | ||
}; | ||
@@ -88,3 +100,3 @@ function resultPrinter(options, source, removedNodes) { | ||
return { | ||
toString: function () { | ||
toString: function() { | ||
if (!removedNodes || removedNodes.length === 0) { | ||
@@ -100,6 +112,9 @@ return source; | ||
var node = removedNodes[i]; | ||
result += source.slice(lastPos, node.start); | ||
lastPos = node.end; | ||
result += source.slice(lastPos, startOf(node)); | ||
lastPos = endOf(node); | ||
if (typeof node.__spliceValue === 'string') { | ||
result += node.__spliceValue; | ||
} | ||
if (!pretty) { | ||
var toReplace = source.slice(node.start, node.end); | ||
var toReplace = source.slice(startOf(node), endOf(node)); | ||
if (!node.loc || node.loc.start.line === node.loc.end.line) { | ||
@@ -117,13 +132,13 @@ result += space(toReplace.length); | ||
return result += source.slice(lastPos); | ||
return (result += source.slice(lastPos)); | ||
}, | ||
generateMap: function () { | ||
generateMap: function() { | ||
return { | ||
version: 3, | ||
sources: [ 'source.js' ], | ||
sources: ['source.js'], | ||
names: [], | ||
mappings: pretty ? generateSourceMappings(removedNodes) : '' | ||
mappings: pretty ? generateSourceMappings(removedNodes) : '', | ||
}; | ||
} | ||
} | ||
}, | ||
}; | ||
} | ||
@@ -138,3 +153,3 @@ | ||
DeclareFunction: removeNode, | ||
DeclareInterface:removeNode, | ||
DeclareInterface: removeNode, | ||
DeclareModule: removeNode, | ||
@@ -152,9 +167,9 @@ DeclareTypeAlias: removeNode, | ||
Identifier: function (context, node, ast) { | ||
Identifier: function(context, node, ast) { | ||
if (node.optional) { | ||
// Find the optional token. | ||
var idx = findTokenIndex(ast.tokens, node.start); | ||
var idx = findTokenIndex(ast.tokens, startOf(node)); | ||
do { | ||
idx++; | ||
} while (ast.tokens[idx].type.label !== '?'); | ||
} while (getLabel(ast.tokens[idx]) !== '?'); | ||
removeNode(context, ast.tokens[idx]); | ||
@@ -164,9 +179,9 @@ } | ||
ClassProperty: function (context, node) { | ||
ClassProperty: function(context, node) { | ||
if (!node.value) { | ||
return removeNode(context, node) | ||
return removeNode(context, node); | ||
} | ||
}, | ||
ExportNamedDeclaration: function (context, node) { | ||
ExportNamedDeclaration: function(context, node) { | ||
if (node.exportKind === 'type' || node.exportKind === 'typeof') { | ||
@@ -177,3 +192,3 @@ return removeNode(context, node); | ||
ImportDeclaration: function (context, node) { | ||
ImportDeclaration: function(context, node) { | ||
if (node.importKind === 'type' || node.importKind === 'typeof') { | ||
@@ -186,11 +201,26 @@ return removeNode(context, node); | ||
if (node.importKind === 'type' || node.importKind === 'typeof') { | ||
var ast = context.ast; | ||
// Flow quirk: Remove importKind which is outside the node | ||
var idxStart = findTokenIndex(ast.tokens, startOf(node)); | ||
var maybeImportKind = ast.tokens[idxStart - 1]; | ||
var maybeImportKindLabel = getLabel(maybeImportKind); | ||
if ( | ||
maybeImportKindLabel === 'type' || | ||
maybeImportKindLabel === 'typeof' | ||
) { | ||
removeNode(context, maybeImportKind); | ||
} | ||
// Remove the node itself | ||
removeNode(context, node); | ||
// Remove trailing comma | ||
var ast = context.ast; | ||
var idx = findTokenIndex(ast.tokens, node.end); | ||
var idx = findTokenIndex(ast.tokens, endOf(node)); | ||
while (isComment(ast.tokens[idx])) { | ||
// NOTE: ast.tokens has no comments in Flow | ||
idx++; | ||
} | ||
if (ast.tokens[idx].type.label === ',') { | ||
if (getLabel(ast.tokens[idx]) === ',') { | ||
removeNode(context, ast.tokens[idx]); | ||
@@ -200,3 +230,48 @@ } | ||
} | ||
} | ||
}, | ||
ArrowFunctionExpression: function(context, node) { | ||
// Naively erasing a multi-line return type from an arrow function will | ||
// leave a newline between the parameter list and the arrow, which is not | ||
// valid JS. Detect this here and move the arrow up to the correct line. | ||
if (context.pretty) { | ||
// Pretty-printing solves this naturally. Good, because our arrow-fudging | ||
// below doesn't play nice with source maps... Which are only created when | ||
// using --pretty. | ||
return; | ||
} | ||
var returnType = node.returnType; | ||
if (returnType) { | ||
var ast = context.ast; | ||
var paramEndIdx = findTokenIndex(ast.tokens, startOf(returnType)); | ||
do { | ||
paramEndIdx--; | ||
} while (isComment(ast.tokens[paramEndIdx])); | ||
var arrowIdx = findTokenIndex(ast.tokens, endOf(returnType)); | ||
while (getLabel(ast.tokens[arrowIdx]) !== '=>') { | ||
arrowIdx++; | ||
} | ||
if ( | ||
ast.tokens[paramEndIdx].loc.end.line < | ||
ast.tokens[arrowIdx].loc.start.line | ||
) { | ||
// Insert an arrow immediately after the parameter list. | ||
removeNode( | ||
context, | ||
getSpliceNodeAtPos( | ||
context, | ||
endOf(ast.tokens[paramEndIdx]), | ||
ast.tokens[paramEndIdx].loc.end, | ||
' =>' | ||
) | ||
); | ||
// Delete the original arrow token. | ||
removeNode(context, ast.tokens[arrowIdx]); | ||
} | ||
} | ||
}, | ||
}; | ||
@@ -210,3 +285,3 @@ | ||
var last = node.implements[node.implements.length - 1]; | ||
var idx = findTokenIndex(ast.tokens, first.start); | ||
var idx = findTokenIndex(ast.tokens, startOf(first)); | ||
do { | ||
@@ -216,5 +291,6 @@ idx--; | ||
var lastIdx = findTokenIndex(ast.tokens, last.start); | ||
var lastIdx = findTokenIndex(ast.tokens, startOf(last)); | ||
do { | ||
if (!isComment(ast.tokens[idx])) { | ||
// NOTE: ast.tokens has no comments in Flow | ||
removeNode(context, ast.tokens[idx]); | ||
@@ -237,3 +313,3 @@ } | ||
while (index > 0 && removedNodes[index - 1].end > node.start) { | ||
while (index > 0 && endOf(removedNodes[index - 1]) > startOf(node)) { | ||
index--; | ||
@@ -286,10 +362,10 @@ } | ||
} | ||
return { | ||
return createNode({ | ||
start: start, | ||
end: start + size, | ||
loc: { | ||
start: { line: line, column: column }, | ||
end: { line: line, column: column + size }, | ||
start: {line: line, column: column}, | ||
end: {line: line, column: column + size}, | ||
}, | ||
}; | ||
}); | ||
} | ||
@@ -299,3 +375,3 @@ | ||
var source = context.source; | ||
var end = node.start; | ||
var end = startOf(node); | ||
var start = end; | ||
@@ -306,7 +382,7 @@ while (source[start - 1] === ' ' || source[start - 1] === '\t') { | ||
if (start !== end) { | ||
return { | ||
return createNode({ | ||
start: start, | ||
end: end, | ||
loc: { start: node.loc.start, end: node.loc.start } | ||
}; | ||
loc: {start: node.loc.start, end: node.loc.start}, | ||
}); | ||
} | ||
@@ -317,3 +393,3 @@ } | ||
var source = context.source; | ||
var start = node.end; | ||
var start = endOf(node); | ||
var end = start; | ||
@@ -332,7 +408,7 @@ while (source[end] === ' ' || source[end] === '\t') { | ||
if (isLastNodeRemovedFromLine(context, node)) { | ||
return { | ||
return createNode({ | ||
start: start, | ||
end: end, | ||
loc: { start: node.loc.end, end: node.loc.end } | ||
}; | ||
loc: {start: node.loc.end, end: node.loc.end}, | ||
}); | ||
} | ||
@@ -342,6 +418,17 @@ } | ||
// Creates a zero-width "node" with a value to splice at that position. | ||
// WARNING: This is only safe to use when source maps are off! | ||
function getSpliceNodeAtPos(context, pos, loc, value) { | ||
return createNode({ | ||
start: pos, | ||
end: pos, | ||
loc: {start: loc, end: loc}, | ||
__spliceValue: value, | ||
}); | ||
} | ||
// 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 priorTokenIdx = findTokenIndex(tokens, startOf(node)) - 1; | ||
var token = tokens[priorTokenIdx]; | ||
@@ -351,6 +438,8 @@ 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] | ||
while ( | ||
priorTokenIdx >= 0 && | ||
token.loc.end.line === line && | ||
isRemovedToken(context, token) | ||
) { | ||
token = tokens[--priorTokenIdx]; | ||
} | ||
@@ -369,3 +458,3 @@ | ||
// Find the last removed node which could possibly contain this token. | ||
while (nodeIdx >= 0 && removedNodes[nodeIdx].start > token.start) { | ||
while (nodeIdx >= 0 && startOf(removedNodes[nodeIdx]) > startOf(token)) { | ||
nodeIdx--; | ||
@@ -377,3 +466,3 @@ } | ||
// This token couldn't be removed if not contained within the removed node. | ||
if (nodeIdx === -1 || node.end < token.end) { | ||
if (nodeIdx === -1 || endOf(node) < endOf(token)) { | ||
return false; | ||
@@ -384,4 +473,4 @@ } | ||
var tokens = context.ast.tokens; | ||
var tokenIdx = findTokenIndex(tokens, node.start); | ||
while (tokens[tokenIdx].end <= node.end) { | ||
var tokenIdx = findTokenIndex(tokens, startOf(node)); | ||
while (endOf(tokens[tokenIdx]) <= endOf(node)) { | ||
if (token === tokens[tokenIdx]) { | ||
@@ -396,3 +485,3 @@ return true; | ||
// Given the AST output of babylon parse, walk through in a depth-first order, | ||
// Given the AST output from the parser, walk through in a depth-first order, | ||
// calling methods on the given visitor, providing context as the first argument. | ||
@@ -413,3 +502,3 @@ function visit(ast, context, visitor) { | ||
} else { | ||
var node = parent ? parent[keys[index]] : ast.program; | ||
var node = parent ? parent[keys[index]] : getProgram(ast); | ||
if (node && typeof node === 'object' && (node.type || node.length)) { | ||
@@ -422,3 +511,3 @@ if (node.type) { | ||
} | ||
stack = { parent: parent, keys: keys, index: index, prev: stack }; | ||
stack = {parent: parent, keys: keys, index: index, prev: stack}; | ||
parent = node; | ||
@@ -439,7 +528,7 @@ keys = Object.keys(node); | ||
while (min <= max) { | ||
var ptr = (min + max) / 2 | 0; | ||
var ptr = ((min + max) / 2) | 0; | ||
var token = tokens[ptr]; | ||
if (token.end <= offset) { | ||
if (endOf(token) <= offset) { | ||
min = ptr + 1; | ||
} else if (token.start > offset) { | ||
} else if (startOf(token) > offset) { | ||
max = ptr - 1; | ||
@@ -456,3 +545,3 @@ } else { | ||
function isComment(token) { | ||
return token.type === 'CommentBlock' || token.type === 'CommentLine'; | ||
return token.type === 'Block' || token.type === 'Line'; | ||
} | ||
@@ -462,3 +551,3 @@ | ||
function space(size) { | ||
var sp = ' ' | ||
var sp = ' '; | ||
var result = ''; | ||
@@ -487,3 +576,3 @@ | ||
var end = { line: 1, column: 0 }; | ||
var end = {line: 1, column: 0}; | ||
@@ -498,3 +587,3 @@ for (var i = 0; i < removedNodes.length; i++) { | ||
} | ||
mappings += vlq.encode([ start.column, 0, lineDiff, columnDiff ]); | ||
mappings += vlq.encode([start.column, 0, lineDiff, columnDiff]); | ||
} else if (columnDiff) { | ||
@@ -504,3 +593,3 @@ if (i) { | ||
} | ||
mappings += vlq.encode([ columnDiff, 0, lineDiff, columnDiff ]); | ||
mappings += vlq.encode([columnDiff, 0, lineDiff, columnDiff]); | ||
} | ||
@@ -510,3 +599,8 @@ | ||
mappings += ','; | ||
mappings += vlq.encode([ 0, 0, end.line - start.line, end.column - start.column ]); | ||
mappings += vlq.encode([ | ||
0, | ||
0, | ||
end.line - start.line, | ||
end.column - start.column, | ||
]); | ||
} | ||
@@ -516,1 +610,34 @@ | ||
} | ||
/** | ||
* A lightweight layer to abstract over the slightly different ASTs returned by | ||
* Flow vs Babylon. | ||
*/ | ||
function startOf(token) { | ||
return token.range[0]; | ||
} | ||
function endOf(token) { | ||
return token.range[1]; | ||
} | ||
function getComments(ast) { | ||
return ast.comments; | ||
} | ||
function createNode(data) { | ||
return { | ||
range: [data.start, data.end], | ||
loc: data.loc, | ||
__spliceValue: data.__spliceValue, | ||
}; | ||
} | ||
function getLabel(token) { | ||
return token.value; | ||
} | ||
function getProgram(ast) { | ||
return ast; | ||
} |
{ | ||
"name": "flow-remove-types", | ||
"version": "1.2.3", | ||
"version": "2.99.0", | ||
"description": "Removes Flow type annotations from JavaScript files with speed and simplicity.", | ||
"author": "Lee Byron <lee@leebyron.com> (http://leebyron.com/)", | ||
"license": "BSD-3-Clause", | ||
"author": { | ||
"name": "Flow Team", | ||
"email": "flow@fb.com" | ||
}, | ||
"contributors": [ | ||
"Lee Byron <lee@leebyron.com> (http://leebyron.com/)" | ||
], | ||
"license": "MIT", | ||
"main": "index.js", | ||
@@ -17,17 +23,15 @@ "bin": { | ||
"flow-node", | ||
"LICENSE", | ||
"PATENTS" | ||
"LICENSE" | ||
], | ||
"homepage": "https://github.com/flowtype/flow-remove-types", | ||
"homepage": "https://flow.org", | ||
"bugs": { | ||
"url": "https://github.com/flowtype/flow-remove-types/issues" | ||
"url": "https://github.com/facebook/flow/issues" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "http://github.com/flowtype/flow-remove-types.git" | ||
"url": "https://github.com/facebook/flow.git" | ||
}, | ||
"scripts": { | ||
"test": "./test.sh", | ||
"test-update": "./test-update.sh", | ||
"postpublish": "./publish-npm-alias.sh" | ||
"test-update": "./test-update.sh" | ||
}, | ||
@@ -43,5 +47,9 @@ "keywords": [ | ||
"dependencies": { | ||
"babylon": "^6.15.0", | ||
"flow-parser": "^0.99.0", | ||
"pirates": "^3.0.2", | ||
"vlq": "^0.2.1" | ||
}, | ||
"engines": { | ||
"node": ">=4" | ||
} | ||
} |
@@ -16,3 +16,3 @@ flow-remove-types | ||
done via adding a plugin to your [Babel](https://babeljs.io/) configuration, | ||
however Babel may be overkill if you're only targetting modern versions of | ||
however Babel may be overkill if you're only targeting modern versions of | ||
Node.js or just not using the modern ES2015 features that may not be in | ||
@@ -128,6 +128,6 @@ every browser. | ||
Built atop the excellent [`babylon`](https://github.com/babel/babylon) parser, | ||
`flow-remove-types` shares the same parse rules as the source of truth as | ||
Flow Babel plugins. It also passes through other common non-standard syntax such | ||
as [JSX](https://facebook.github.io/jsx/) and experimental ECMAScript proposals. | ||
Built atop the official Flow [parser](https://github.com/facebook/flow/tree/master/packages/flow-parser), | ||
`flow-remove-types` is designed to operate on the same syntax Flow itself understands. | ||
It also passes through other common non-standard syntax such as [JSX](https://facebook.github.io/jsx/) | ||
and experimental ECMAScript proposals that Flow supports. | ||
@@ -201,2 +201,4 @@ **Before:** | ||
> *NOTE*: These timings are for `flow-remove-types` v1. | ||
### Install: | ||
@@ -203,0 +205,0 @@ |
@@ -0,2 +1,10 @@ | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
var flowRemoveTypes = require('./index'); | ||
var pirates = require('pirates'); | ||
@@ -14,31 +22,31 @@ // Supported options: | ||
options = newOptions; | ||
} | ||
}; | ||
// Swizzle Module#_compile on each applicable module instance. | ||
// NOTE: if using alongside Babel or another require-hook which simply | ||
// over-writes the require.extensions and does not continue execution, then | ||
// this require hook must come after it. Encourage those module authors to call | ||
// the prior loader in their require hooks. | ||
var jsLoader = require.extensions['.js']; | ||
var exts = [ '.js', '.mjs', '.jsx', '.flow', '.es6' ]; | ||
exts.forEach(function (ext) { | ||
var superLoader = require.extensions[ext] || jsLoader; | ||
require.extensions[ext] = function (module, filename) { | ||
if (shouldTransform(filename, options)) { | ||
var super_compile = module._compile; | ||
module._compile = function _compile(code, filename) { | ||
super_compile.call(this, flowRemoveTypes(code, options).toString(), filename); | ||
}; | ||
var exts = ['.js', '.mjs', '.jsx', '.flow', '.es6']; | ||
var revert = pirates.addHook( | ||
function hook(code, filename) { | ||
try { | ||
return flowRemoveTypes(code, options).toString(); | ||
} catch (e) { | ||
e.message = filename + ': ' + e.message; | ||
throw e; | ||
} | ||
superLoader(module, filename); | ||
}; | ||
}); | ||
}, | ||
{exts: exts, matcher: shouldTransform} | ||
); | ||
function shouldTransform(filename, options) { | ||
function shouldTransform(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)); | ||
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)) | ||
); | ||
} | ||
@@ -65,4 +73,6 @@ | ||
throw new Error( | ||
'flow-remove-types: includes and excludes must be RegExp or path strings. Got: ' + pattern | ||
'flow-remove-types: ' + | ||
'includes and excludes must be RegExp or path strings. Got: ' + | ||
pattern | ||
); | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
39755
603
248
3
7
1
+ Addedflow-parser@^0.99.0
+ Addedpirates@^3.0.2
+ Addedflow-parser@0.99.1(transitive)
+ Addednode-modules-regexp@1.0.0(transitive)
+ Addedpirates@3.0.2(transitive)
- Removedbabylon@^6.15.0
- Removedbabylon@6.18.0(transitive)