diff --git a/packages/flow-dev-tools/src/__tests__/all-tests.js b/packages/flow-dev-tools/src/__tests__/all-tests.js index 608797c53d8..19e885aa768 100644 --- a/packages/flow-dev-tools/src/__tests__/all-tests.js +++ b/packages/flow-dev-tools/src/__tests__/all-tests.js @@ -79,7 +79,7 @@ const x = 4; [], ['foo', 'bar'], ); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('updateSuppressionsInText does not remove eslint-disable multiline comment', async () => { @@ -106,7 +106,7 @@ const x = 4; [], ['foo', 'bar'], ); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('exec', async () => { @@ -131,8 +131,6 @@ test('splitIntoChunks', () => { expect(splitIntoChunks('✓✓✓✓✓', 1)).toEqual(['✓', '✓', '✓', '✓', '✓']); }); -const flowBinPath = path.resolve(process.env.FLOW_BIN); - test('updateSuppressionsInText removes unused comments', async () => { const testInput = ` // $FlowFixMe @@ -152,7 +150,7 @@ const x = 4; [], ['foo', 'bar'], ); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('updateSuppressionsInText removes unused comments with sites', async () => { @@ -174,7 +172,7 @@ const x = 4; [], ['foo', 'bar'], ); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('updateSuppressionsInText can add site', async () => { @@ -191,7 +189,7 @@ const x = 4; `.trimLeft(); const errorsByLine = addErrorByLine(new Map(), testInput, loc, [], ['foo']); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('updateSuppressionsInText keeps unknown sites', async () => { @@ -214,7 +212,7 @@ const x = 4; [], ['foo', 'bar'], ); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('updateSuppressionsInText keeps unknown sites (2)', async () => { @@ -237,7 +235,7 @@ const x = 4; [], ['foo', 'bar'], ); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('updateSuppressionsInText adds site and keeps unknown sites', async () => { @@ -254,7 +252,7 @@ const x = 4; `.trimLeft(); const errorsByLine = addErrorByLine(new Map(), testInput, loc, [], ['foo']); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('updateSuppressionsInText handles JSX children', async () => { @@ -276,13 +274,7 @@ test('updateSuppressionsInText handles JSX children', async () => { `.trimLeft(); const errorsByLine = addErrorByLine(new Map(), testInput, loc, ['code'], []); - await expectUpdatedComments( - testInput, - testOutput, - errorsByLine, - flowBinPath, - [], - ); + await expectUpdatedComments(testInput, testOutput, errorsByLine, []); }); test('updateSuppressionsInText handles JSX children with multiline open tag', async () => { @@ -306,13 +298,7 @@ test('updateSuppressionsInText handles JSX children with multiline open tag', as `.trimLeft(); const errorsByLine = addErrorByLine(new Map(), testInput, loc, ['code'], []); - await expectUpdatedComments( - testInput, - testOutput, - errorsByLine, - flowBinPath, - [], - ); + await expectUpdatedComments(testInput, testOutput, errorsByLine, []); }); function addErrorByLine(errorsByLine, contents, loc, errorCodes, unusedRoots) { @@ -352,9 +338,11 @@ function posToOffset(contents, line, col) { // Using 1-indexed line and column for this let currentLine = 1; let currentCol = 1; - const buf = Buffer.from(contents, 'utf8'); - while (offset < buf.length && !(currentLine === line && currentCol === col)) { - const char = buf.toString('utf8', offset, offset + 1); + while ( + offset < contents.length && + !(currentLine === line && currentCol === col) + ) { + const char = contents[offset]; if (char === '\n') { currentLine++; currentCol = 1; @@ -371,25 +359,20 @@ async function expectUpdatedComments( input, expectedOutput, errorsByLine, - flowBinPath, sites = ['foo', 'bar'], ) { const actualOutput = await updateSuppressionsInText( - Buffer.from(input), + input, new Set(sites), 1, errorsByLine, '', - flowBinPath, ); - expect(actualOutput.toString()).toEqual(expectedOutput); + expect(actualOutput).toEqual(expectedOutput); } test('addCommentsToCode > basic', async () => { - expect(await addCommentsToCode('foobar', null, '', [], flowBinPath)).toEqual([ - '', - 0, - ]); + expect(await addCommentsToCode('foobar', null, '', [])).toEqual(['', 0]); }); test('addCommentsToCode > advanced', async () => { @@ -400,7 +383,6 @@ test('addCommentsToCode > advanced', async () => { testInput, /* Intentionally made these out of order to test that they are still inserted properly */ [1, 6, 5, 3].map(line => makeSuppression(line, testInput)), - flowBinPath, ), ).toEqual([testOutput, 8]); }); @@ -453,7 +435,6 @@ function bar(): b {} error_codes: ['code4'], }, ], - flowBinPath, ), ).toEqual([ `// $FlowFixMe[code1] @@ -471,23 +452,17 @@ function bar(): b {} test('addCommentsToCode > does not touch AST when no errors match the given code', async () => { expect( - await addCommentsToCode( - '', - 'code2', - `function foo() {}`, - [ - { - loc: { - start: {line: 1, column: 13, offset: 13}, - end: {line: 1, column: 14, offset: 14}, - }, - isError: true, - lints: new Set(), - error_codes: ['code1'], + await addCommentsToCode('', 'code2', `function foo() {}`, [ + { + loc: { + start: {line: 1, column: 13, offset: 13}, + end: {line: 1, column: 14, offset: 14}, }, - ], - flowBinPath, - ), + isError: true, + lints: new Set(), + error_codes: ['code1'], + }, + ]), ).toEqual([`function foo() {}`, 0]); }); @@ -519,7 +494,6 @@ function bar(): b {} error_codes: ['code2'], }, ], - flowBinPath, ), ).toEqual([ `function foo() {} @@ -554,7 +528,6 @@ class A { error_codes: ['code1'], }, ], - flowBinPath, ), ).toEqual([ ` @@ -590,7 +563,6 @@ class Foo { error_codes: ['code1'], }, ], - flowBinPath, ), ).toEqual([ ` @@ -667,7 +639,6 @@ test('addCommentsToCode > JSX', async () => { error_codes: ['code7'], }, ], - flowBinPath, ), ).toEqual([ `function A(): React.Node { @@ -769,8 +740,6 @@ function locForLine(line, text) { } test('removeUnusedErrorSuppressionsFromText', async () => { - const flowBinPath = path.resolve(process.env.FLOW_BIN); - const testInput = ` // single line comment with some whitespace const bar = 4; /* multiline @@ -805,12 +774,10 @@ const foo = 4; [7, 4, 8, 20], [], ].map(args => makeLoc(testInput, ...args)); - await expectCommentsAreRemoved(testInput, testOutput, errorLocs, flowBinPath); + await expectCommentsAreRemoved(testInput, testOutput, errorLocs); }); test('removeExtraSpaceWhenRemovingUnusedFlowLint', async () => { - const flowBinPath = path.resolve(process.env.FLOW_BIN); - const testInput = `// flowlint foo bar //flowlint foo bar // flowlint foo bar @@ -844,12 +811,10 @@ test('removeExtraSpaceWhenRemovingUnusedFlowLint', async () => { [8, 22, 8, 25], [9, 27, 9, 30], ].map(args => makeLoc(testInput, ...args)); - await expectCommentsAreRemoved(testInput, testOutput, errorLocs, flowBinPath); + await expectCommentsAreRemoved(testInput, testOutput, errorLocs); }); test('deleteUnusedFlowLintComments', async () => { - const flowBinPath = path.resolve(process.env.FLOW_BIN); - const testInput = `// flowlint foo bar //flowlint foo bar // flowlint-next-line foo bar @@ -917,12 +882,10 @@ let x = [19, 15, 19, 18], [21, 6, 21, 9], ].map(args => makeLoc(testInput, ...args)); - await expectCommentsAreRemoved(testInput, testOutput, errorLocs, flowBinPath); + await expectCommentsAreRemoved(testInput, testOutput, errorLocs); }); test('unicode', async () => { - const flowBinPath = path.resolve(process.env.FLOW_BIN); - const testInput = `const unicode = "\u{714E}\u{8336}"; const smiley = "\uD83D\uDE00"; @@ -992,21 +955,15 @@ let x = [21, 17, 21, 20], [22, 15, 22, 18], ].map(args => makeLoc(testInput, ...args)); - await expectCommentsAreRemoved(testInput, testOutput, errorLocs, flowBinPath); + await expectCommentsAreRemoved(testInput, testOutput, errorLocs); }); -async function expectCommentsAreRemoved( - input, - expectedOutput, - errorLocs, - flowBinPath, -) { +async function expectCommentsAreRemoved(input, expectedOutput, errorLocs) { const actualOutput = await removeUnusedErrorSuppressionsFromText( - Buffer.from(input), + input, errorLocs, - flowBinPath, ); - expect(actualOutput.toString()).toEqual(expectedOutput); + expect(actualOutput).toEqual(expectedOutput); } (async () => { diff --git a/packages/flow-dev-tools/src/comment/add-commentsRunner.js b/packages/flow-dev-tools/src/comment/add-commentsRunner.js index dfa640ae6fa..a976519523c 100644 --- a/packages/flow-dev-tools/src/comment/add-commentsRunner.js +++ b/packages/flow-dev-tools/src/comment/add-commentsRunner.js @@ -147,7 +147,6 @@ async function addCommentsToSource( args.error_code, codeString, locs, - args.bin, ); await writeFile(source, code); return commentCount; @@ -160,13 +159,7 @@ function addCommentsToCodeInternal( path: Array, ) { const [inside, ast] = getContext(loc, path); - return addCommentToText( - Buffer.from(code), - loc, - inside, - comments, - ast, - ).toString(); + return addCommentToText(code, loc, inside, comments, ast).toString(); } async function addCommentsToCode( @@ -174,13 +167,12 @@ async function addCommentsToCode( error_code: ?string, code: string, locs: Array, - flowBinPath: string, ): Promise< [string, number], > /* [resulting code, number of comments inserted] */ { locs.sort((l1, l2) => l2.loc.start.line - l1.loc.start.line); - const ast = await getAst(code, flowBinPath); + const ast = await getAst(code); let commentCount = 0; for (let {loc, isError, lints, error_codes} of locs) { diff --git a/packages/flow-dev-tools/src/comment/commentMutator.js b/packages/flow-dev-tools/src/comment/commentMutator.js index b56595102a3..7569d11e47d 100644 --- a/packages/flow-dev-tools/src/comment/commentMutator.js +++ b/packages/flow-dev-tools/src/comment/commentMutator.js @@ -15,10 +15,6 @@ const {getNodeAtRange} = require('./getPathToLoc'); const {NORMAL, JSX, JSX_FRAGMENT, TEMPLATE} = require('./getContext'); const {format} = require('util'); -function bufferCharAt(buf: Buffer, pos: number): string { - return buf.toString('utf8', pos, pos + 1); -} - const flowlintRegex = /^[ \t\n\r*]*flowlint(-line|-next-line)?\b/; function isLintSuppression(commentAST: {value: string}): boolean { return flowlintRegex.test(commentAST.value); @@ -45,7 +41,7 @@ const edible = /[\t ]/; * in the case where there is nothing before or after it */ function expandComment( - contents: Buffer, + contents: string, startOffset: number, endOffset: number, commentAST: {value: string, range: [number, number]} | void, @@ -89,21 +85,18 @@ function expandComment( // Find the next interesting characters before and after the removed comment let beforeStart = origBeforeStart; let afterEnd = origAfterEnd; - while ( - beforeStart >= 0 && - bufferCharAt(contents, beforeStart).match(edible) - ) { + while (beforeStart >= 0 && contents[beforeStart].match(edible)) { beforeStart--; } - while (afterEnd < length && bufferCharAt(contents, afterEnd).match(edible)) { + while (afterEnd < length && contents[afterEnd].match(edible)) { afterEnd++; } if ( beforeStart >= 0 && afterEnd < length && - bufferCharAt(contents, beforeStart) === '{' && - bufferCharAt(contents, afterEnd) === '}' + contents[beforeStart] === '{' && + contents[afterEnd] === '}' ) { // If this is JSX, then the curly braces start and stop a JSXExpressionContainer const node = getNodeAtRange([beforeStart, afterEnd + 1], ast); @@ -116,24 +109,18 @@ function expandComment( origBeforeStart = beforeStart; origAfterEnd = afterEnd; - while ( - beforeStart >= 0 && - bufferCharAt(contents, beforeStart).match(edible) - ) { + while (beforeStart >= 0 && contents[beforeStart].match(edible)) { beforeStart--; } - while ( - afterEnd < length && - bufferCharAt(contents, afterEnd).match(edible) - ) { + while (afterEnd < length && contents[afterEnd].match(edible)) { afterEnd++; } } } - if (beforeStart < 0 || bufferCharAt(contents, beforeStart) === '\n') { + if (beforeStart < 0 || contents[beforeStart] === '\n') { // There's nothing before the removed comment on this line - if (afterEnd > length || bufferCharAt(contents, afterEnd) === '\n') { + if (afterEnd > length || contents[afterEnd] === '\n') { // The line is completely empty. Let's remove a newline from the start or // end of the line if (afterEnd < length) { @@ -146,7 +133,7 @@ function expandComment( // preceding whitespace thanks to indentation beforeStart = origBeforeStart; } - } else if (afterEnd >= length || bufferCharAt(contents, afterEnd) === '\n') { + } else if (afterEnd >= length || contents[afterEnd] === '\n') { // There's something preceding the comment but nothing afterwards. We can // just remove the rest of the line } else { @@ -158,47 +145,40 @@ function expandComment( return [beforeStart + 1, afterEnd]; } -function findStartOfLine(contents: Buffer, startOffset: number): number { +function findStartOfLine(contents: string, startOffset: number): number { // if startOffset is already a newline, that's not the start of the line, // it's the end of the line. so start from the character before. let start = startOffset - 1; - while (start >= 0 && !bufferCharAt(contents, start).match(newlineRegex)) { + while (start >= 0 && !contents[start].match(newlineRegex)) { start--; } return start + 1; } -function findEndOfLine(contents: Buffer, startOffset: number): number { +function findEndOfLine(contents: string, startOffset: number): number { let start = startOffset; - while ( - start < contents.length && - !bufferCharAt(contents, start).match(newlineRegex) - ) { + while (start < contents.length && !contents[start].match(newlineRegex)) { start++; } return start; } function insertCommentToText( - contents: Buffer, + contents: string, startOffset: number, comment: string, -): Buffer { - return Buffer.concat([ - contents.slice(0, startOffset), - Buffer.from(comment), - contents.slice(startOffset), - ]); +): string { + return contents.slice(0, startOffset) + comment + contents.slice(startOffset); } function addCommentToText( - contents: Buffer, + contents: string, loc: FlowLoc, inside: Context, comments: Array, ast: any, startOfLine?: number, -): Buffer { +): string { let startOffset; let start; if (startOfLine == null) { @@ -210,7 +190,7 @@ function addCommentToText( } const endOfLine = findEndOfLine(contents, startOffset); - let line = contents.toString('utf8', start, endOfLine); + let line = contents.slice(start, endOfLine); const inJSX = inside === JSX_FRAGMENT || inside === JSX; if (inside === NORMAL) { return insertCommentToText( @@ -273,11 +253,11 @@ function addCommentToText( newCodeParts.push(padding + part2); } - return Buffer.concat([ - contents.slice(0, start), - Buffer.from(newCodeParts.join('\n')), - contents.slice(endOfLine), - ]); + return ( + contents.slice(0, start) + + newCodeParts.join('\n') + + contents.slice(endOfLine) + ); } else if (inJSX && ast.type === 'JSXText') { /* Ignore the case where the error's loc starts after the last non-whitespace * character of the line. This can occur when an error's loc spans the @@ -294,13 +274,9 @@ function addCommentToText( let firstNonWhitespaceCharacter = startOffset; let atEndOfLine = true; while (firstNonWhitespaceCharacter < contents.length) { - if ( - bufferCharAt(contents, firstNonWhitespaceCharacter).match(newlineRegex) - ) { + if (contents[firstNonWhitespaceCharacter].match(newlineRegex)) { break; - } else if ( - bufferCharAt(contents, firstNonWhitespaceCharacter).match(edible) - ) { + } else if (contents[firstNonWhitespaceCharacter].match(edible)) { firstNonWhitespaceCharacter++; } else { atEndOfLine = false; @@ -328,12 +304,12 @@ function addCommentToText( } function removeUnusedErrorSuppressionFromText( - contents: Buffer, + contents: string, startOffset: number, endOffset: number, commentAST: Object | void, ast: Object, -): Buffer { +): string { // remove the comment and surrounding whitespace let [start, end] = expandComment( contents, @@ -343,7 +319,7 @@ function removeUnusedErrorSuppressionFromText( ast, ); - return Buffer.concat([contents.slice(0, start), contents.slice(end)]); + return contents.slice(0, start) + contents.slice(end); } /* Take up to `max` characters from str, trying to split at a space or dash or diff --git a/packages/flow-dev-tools/src/comment/getAst.js b/packages/flow-dev-tools/src/comment/getAst.js index 7c30101ea9f..c5417b32cdf 100644 --- a/packages/flow-dev-tools/src/comment/getAst.js +++ b/packages/flow-dev-tools/src/comment/getAst.js @@ -8,18 +8,12 @@ * @format */ +const HermesParser = require('hermes-parser'); const {format} = require('util'); const {exec} = require('../utils/async'); -async function getAst( - code: string, - flowBinPath: string, -): Promise /* AST */ { - const stdout = await exec(format('%s ast', flowBinPath), { - maxBuffer: 16 * 1024 * 1024, - stdin: code, - }); - return JSON.parse(stdout); +function getAst(code: string): Object /* AST */ { + return HermesParser.parse(code, {babel: false}); } module.exports = { diff --git a/packages/flow-dev-tools/src/comment/remove-commentsRunner.js b/packages/flow-dev-tools/src/comment/remove-commentsRunner.js index 0a811eaef11..8da2ce63326 100644 --- a/packages/flow-dev-tools/src/comment/remove-commentsRunner.js +++ b/packages/flow-dev-tools/src/comment/remove-commentsRunner.js @@ -33,29 +33,26 @@ async function getErrors(args: Args): Promise>> { async function removeUnusedErrorSuppressions( filename: string, errors: Array, - flowBinPath: string, ): Promise { const contentsString = await readFile(filename, 'utf8'); const contents = await removeUnusedErrorSuppressionsFromText( - Buffer.from(contentsString, 'utf8'), + contentsString, errors, - flowBinPath, ); - await writeFile(filename, contents.toString('utf8')); + await writeFile(filename, contents); } // Exported for testing async function removeUnusedErrorSuppressionsFromText( - contents: Buffer, + contents: string, errors: Array, - flowBinPath: string, -): Promise { +): Promise { // Sort in reverse order so that we remove comments later in the file first. Otherwise, the // removal of comments earlier in the file would outdate the locations for comments later in the // file. errors.sort((loc1, loc2) => loc2.start.offset - loc1.start.offset); - const ast = await getAst(contents.toString('utf8'), flowBinPath); + const ast = await getAst(contents); for (const error of errors) { const origStart = error.start.offset; @@ -110,7 +107,7 @@ async function runner(args: Args): Promise { }); await Promise.all( errors.map(([filename, errors]) => - removeUnusedErrorSuppressions(filename, errors, args.bin), + removeUnusedErrorSuppressions(filename, errors), ), ); console.log( diff --git a/packages/flow-dev-tools/src/update-suppressions/update-suppressionsRunner.js b/packages/flow-dev-tools/src/update-suppressions/update-suppressionsRunner.js index 5c2c99c4306..21940d30cf7 100644 --- a/packages/flow-dev-tools/src/update-suppressions/update-suppressionsRunner.js +++ b/packages/flow-dev-tools/src/update-suppressions/update-suppressionsRunner.js @@ -201,14 +201,14 @@ function replaceSites(text: string, sites: $ReadOnlyArray): string { * comment if it's unused everywhere. */ function updateErrorSuppression( - contents: Buffer, + contents: string, startOffset: number, endOffset: number, commentAST: Object | void, ast: Object, knownRoots: Set, unusedRoots: Set, -): Buffer { +): string { let commentStartOffset; let commentEndOffset; if (commentAST) { @@ -218,7 +218,7 @@ function updateErrorSuppression( commentEndOffset = endOffset; } let innerOffset = commentStartOffset + 2; // `/*` and `//` are both 2 chars - let text = contents.slice(innerOffset, endOffset).toString('utf8'); + let text = contents.slice(innerOffset, endOffset); // do not remove the comment if it's a eslint suppression // TODO update the logging provided in the end of the command @@ -250,11 +250,7 @@ function updateErrorSuppression( text = replaceSites(text, [...roots]); } - return Buffer.concat([ - contents.slice(0, innerOffset), - Buffer.from(text), - contents.slice(endOffset), - ]); + return contents.slice(0, innerOffset) + text + contents.slice(endOffset); } } @@ -264,36 +260,33 @@ async function updateSuppressions( numBins: number, errors: Map, comment: string, - flowBinPath: string, ): Promise { const contentsString = await readFile(filename, 'utf8'); const contents = await updateSuppressionsInText( - Buffer.from(contentsString, 'utf8'), + contentsString, allRoots, numBins, errors, comment, - flowBinPath, ); - await writeFile(filename, contents.toString('utf8')); + await writeFile(filename, contents); } // Exported for testing async function updateSuppressionsInText( - contents: Buffer, + contents: string, allRoots: Set, numBins: number, errorsByLine: Map, comment: string, - flowBinPath: string, -): Promise { +): Promise { const errors = Array.from(errorsByLine); // Sort in reverse order so that we remove comments later in the file first. Otherwise, the // removal of comments earlier in the file would outdate the locations for comments later in the // file. errors.sort(([line1, _errs1], [line2, _errs2]) => line2 - line1); - const ast = await getAst(contents.toString('utf8'), flowBinPath); + const ast = await getAst(contents); for (const [ _line, @@ -432,7 +425,6 @@ async function runner(args: Args): Promise { args.diffBin ? 2 : 1, errors, args.comment, - args.bin, ); }), );