From b5c23c79174682452ccd46ec2feddc49448880a5 Mon Sep 17 00:00:00 2001 From: Andre Wachsmuth Date: Wed, 27 Apr 2022 15:34:13 +0200 Subject: [PATCH] Preserve directive prologues fixes #5 #7 (#6) * Preserve directive prologues, fixes #5 The most important goal is correctness. Directives were destroyed by placing imports above it. Directive prologues are defined by the spec as > A Directive Prologue is the longest sequence of ExpressionStatements occurring as > the initial StatementListItems or ModuleItems of a FunctionBody, a ScriptBody, or > a ModuleBody and where each ExpressionStatement in the sequence consists entirely > of a StringLiteral token followed by a semicolon. The semicolon may appear explicitly > or may be inserted by automatic semicolon insertion (12.9). A Directive Prologue may > be an empty sequence. * Remove nodes from code via proper algorithm, fixes #7 Previously, a regex string replacement was used, and regex is an inadequate tool for transforming entire JavaScript code files. Instead, this commit changes that to a proper algorithm that removes the code at the source position ranges of the nodes to be removed. This will not work properly if the Babel parser ever did not return the correct source positions, but that is not a regression since the former algortihm already relied on the positions being correct. * Remove comments from directives we add back at the top, #5 The comments are added back as part of the new directives placed at the top, so we should remove them from the original code we add after the directives and the imports. --- src/preprocessor.ts | 10 +- src/utils/__tests__/get-code-from-ast.spec.ts | 2 +- .../remove-nodes-from-original-code.spec.ts | 5 +- src/utils/get-all-comments-from-nodes.ts | 4 +- src/utils/get-code-from-ast.ts | 17 +- src/utils/get-import-nodes.ts | 5 +- src/utils/get-sorted-nodes.ts | 5 +- src/utils/remove-nodes-from-original-code.ts | 197 +++++++++++++++--- .../__snapshots__/ppsi.spec.js.snap | 132 ++++++++++++ .../comments-on-directive-are-preserved.ts | 13 ++ .../custom-directive-remains-at-top.ts | 10 + .../multiple-directives-remain-at-top.ts | 11 + tests/PreserveStrict/ppsi.spec.js | 5 + .../strict-directive-remains-at-top.ts | 10 + .../string-literal-at-top-is-pushed-down.ts | 9 + .../__snapshots__/ppsi.spec.js.snap | 36 ++++ ...es-not-remove-content-in-string-literal.ts | 7 + ...does-remove-import-after-inline-comment.ts | 7 + .../ppsi.spec.js | 5 + 19 files changed, 453 insertions(+), 37 deletions(-) create mode 100644 tests/PreserveStrict/__snapshots__/ppsi.spec.js.snap create mode 100644 tests/PreserveStrict/comments-on-directive-are-preserved.ts create mode 100644 tests/PreserveStrict/custom-directive-remains-at-top.ts create mode 100644 tests/PreserveStrict/multiple-directives-remain-at-top.ts create mode 100644 tests/PreserveStrict/ppsi.spec.js create mode 100644 tests/PreserveStrict/strict-directive-remains-at-top.ts create mode 100644 tests/PreserveStrict/string-literal-at-top-is-pushed-down.ts create mode 100644 tests/RemoveStatementsFromOriginalCode/__snapshots__/ppsi.spec.js.snap create mode 100644 tests/RemoveStatementsFromOriginalCode/does-not-remove-content-in-string-literal.ts create mode 100644 tests/RemoveStatementsFromOriginalCode/does-remove-import-after-inline-comment.ts create mode 100644 tests/RemoveStatementsFromOriginalCode/ppsi.spec.js diff --git a/src/preprocessor.ts b/src/preprocessor.ts index d0c06a3..a636ce1 100644 --- a/src/preprocessor.ts +++ b/src/preprocessor.ts @@ -7,7 +7,7 @@ import { getCodeFromAst } from './utils/get-code-from-ast'; import { getExperimentalParserPlugins } from './utils/get-experimental-parser-plugins'; import { getSortedNodes } from './utils/get-sorted-nodes'; -export function preprocessor(code: string, options: PrettierOptions) { +export function preprocessor(code: string, options: PrettierOptions): string { const { importOrderParserPlugins, importOrder, @@ -24,6 +24,8 @@ export function preprocessor(code: string, options: PrettierOptions) { }; const ast = babelParser(code, parserOptions); + + const directives = ast.program.directives; const interpreter = ast.program.interpreter; traverse(ast, { @@ -38,7 +40,9 @@ export function preprocessor(code: string, options: PrettierOptions) { }); // short-circuit if there are no import declaration - if (importNodes.length === 0) return code; + if (importNodes.length === 0) { + return code; + } const allImports = getSortedNodes(importNodes, { importOrder, @@ -48,5 +52,5 @@ export function preprocessor(code: string, options: PrettierOptions) { importOrderSortSpecifiers, }); - return getCodeFromAst(allImports, code, interpreter); + return getCodeFromAst(allImports, code, directives, interpreter); } diff --git a/src/utils/__tests__/get-code-from-ast.spec.ts b/src/utils/__tests__/get-code-from-ast.spec.ts index 23009ce..d64a7d2 100644 --- a/src/utils/__tests__/get-code-from-ast.spec.ts +++ b/src/utils/__tests__/get-code-from-ast.spec.ts @@ -22,7 +22,7 @@ import a from 'a'; importOrderGroupNamespaceSpecifiers: false, importOrderSortSpecifiers: false, }); - const formatted = getCodeFromAst(sortedNodes, code, null); + const formatted = getCodeFromAst(sortedNodes, code, [], undefined); expect(format(formatted, { parser: 'babel' })).toEqual( `// first comment // second comment diff --git a/src/utils/__tests__/remove-nodes-from-original-code.spec.ts b/src/utils/__tests__/remove-nodes-from-original-code.spec.ts index f214cf5..978f6e1 100644 --- a/src/utils/__tests__/remove-nodes-from-original-code.spec.ts +++ b/src/utils/__tests__/remove-nodes-from-original-code.spec.ts @@ -1,3 +1,4 @@ +import { parse as babelParser } from '@babel/parser'; import { format } from 'prettier'; import { getAllCommentsFromNodes } from '../get-all-comments-from-nodes'; @@ -5,7 +6,7 @@ import { getImportNodes } from '../get-import-nodes'; import { getSortedNodes } from '../get-sorted-nodes'; import { removeNodesFromOriginalCode } from '../remove-nodes-from-original-code'; -const code = `// first comment +const code = `"some directive";// first comment // second comment import z from 'z'; import c from 'c'; @@ -18,6 +19,7 @@ import a from 'a'; `; test('it should remove nodes from the original code', () => { + const ast = babelParser(code, { sourceType: 'module' }); const importNodes = getImportNodes(code); const sortedNodes = getSortedNodes(importNodes, { importOrder: [], @@ -31,6 +33,7 @@ test('it should remove nodes from the original code', () => { const commentAndImportsToRemoveFromCode = [ ...sortedNodes, ...allCommentsFromImports, + ...ast.program.directives, ]; const codeWithoutImportDeclarations = removeNodesFromOriginalCode( code, diff --git a/src/utils/get-all-comments-from-nodes.ts b/src/utils/get-all-comments-from-nodes.ts index 25b4e0f..9202012 100644 --- a/src/utils/get-all-comments-from-nodes.ts +++ b/src/utils/get-all-comments-from-nodes.ts @@ -1,6 +1,6 @@ -import { CommentBlock, CommentLine, Statement } from '@babel/types'; +import { CommentBlock, CommentLine, Directive, Statement } from '@babel/types'; -export const getAllCommentsFromNodes = (nodes: Statement[]) => +export const getAllCommentsFromNodes = (nodes: (Directive | Statement)[]) => nodes.reduce((acc, node) => { if ( Array.isArray(node.leadingComments) && diff --git a/src/utils/get-code-from-ast.ts b/src/utils/get-code-from-ast.ts index e3e96a6..753c046 100644 --- a/src/utils/get-code-from-ast.ts +++ b/src/utils/get-code-from-ast.ts @@ -1,5 +1,5 @@ import generate from '@babel/generator'; -import { InterpreterDirective, Statement, file } from '@babel/types'; +import { Directive, InterpreterDirective, Statement, file } from '@babel/types'; import { newLineCharacters } from '../constants'; import { getAllCommentsFromNodes } from './get-all-comments-from-nodes'; @@ -7,20 +7,29 @@ import { removeNodesFromOriginalCode } from './remove-nodes-from-original-code'; /** * This function generate a code string from the passed nodes. - * @param nodes all imports - * @param originalCode + * @param nodes All imports, in the sorted order in which they should appear in + * the generated code. + * @param originalCode The original input code that was passed to this plugin. + * @param directives All directive prologues from the original code (e.g. + * `"use strict";`). + * @param interpreter Optional interpreter directives, if present (e.g. + * `#!/bin/node`). */ export const getCodeFromAst = ( nodes: Statement[], originalCode: string, + directives: Directive[], interpreter?: InterpreterDirective | null, ) => { const allCommentsFromImports = getAllCommentsFromNodes(nodes); + const allCommentsFromDirectives = getAllCommentsFromNodes(directives); const nodesToRemoveFromCode = [ ...nodes, ...allCommentsFromImports, + ...allCommentsFromDirectives, ...(interpreter ? [interpreter] : []), + ...directives, ]; const codeWithoutImportsAndInterpreter = removeNodesFromOriginalCode( @@ -31,7 +40,7 @@ export const getCodeFromAst = ( const newAST = file({ type: 'Program', body: nodes, - directives: [], + directives: directives, sourceType: 'module', interpreter: interpreter, sourceFile: '', diff --git a/src/utils/get-import-nodes.ts b/src/utils/get-import-nodes.ts index 5530a35..139dd6d 100644 --- a/src/utils/get-import-nodes.ts +++ b/src/utils/get-import-nodes.ts @@ -2,7 +2,10 @@ import { ParserOptions, parse as babelParser } from '@babel/parser'; import traverse, { NodePath } from '@babel/traverse'; import { ImportDeclaration, isTSModuleDeclaration } from '@babel/types'; -export const getImportNodes = (code: string, options?: ParserOptions) => { +export const getImportNodes = ( + code: string, + options?: ParserOptions, +): ImportDeclaration[] => { const importNodes: ImportDeclaration[] = []; const ast = babelParser(code, { ...options, diff --git a/src/utils/get-sorted-nodes.ts b/src/utils/get-sorted-nodes.ts index 0bbd89c..ca9c0df 100644 --- a/src/utils/get-sorted-nodes.ts +++ b/src/utils/get-sorted-nodes.ts @@ -17,7 +17,10 @@ import { getSortedNodesByImportOrder } from './get-sorted-nodes-by-import-order' * @param nodes All import nodes that should be sorted. * @param options Options to influence the behavior of the sorting algorithm. */ -export const getSortedNodes: GetSortedNodes = (nodes, options) => { +export const getSortedNodes: GetSortedNodes = ( + nodes, + options, +): ImportOrLine[] => { const { importOrderSeparation } = options; // Split nodes at each boundary between a side-effect node and a diff --git a/src/utils/remove-nodes-from-original-code.ts b/src/utils/remove-nodes-from-original-code.ts index ba1a8d0..c31c480 100644 --- a/src/utils/remove-nodes-from-original-code.ts +++ b/src/utils/remove-nodes-from-original-code.ts @@ -1,20 +1,165 @@ import { CommentBlock, CommentLine, + Directive, ImportDeclaration, InterpreterDirective, Statement, } from '@babel/types'; -/** Escapes a string literal to be passed to new RegExp. See: https://stackoverflow.com/a/6969486/480608. - * @param s the string to escape +/** A range between a start position (inclusive) and an end position (exclusive). */ +type Range = readonly [start: number, end: number]; + +/** An optional range between a start position (inclusive) and an end position (exclusive). */ +type OptionalRange = readonly [start: number | null, end: number | null]; + +/** Compares two range by their start position. */ +function compareRangesByStart(range1: Range, range2: Range): number { + return range1[0] < range2[0] ? -1 : range1[0] > range2[0] ? 1 : 0; +} + +/** Type predicate that checks whether a range has a defined start and end. */ +function hasRange(range: OptionalRange): range is Range { + return ( + range[0] !== null && + range[1] !== null && + Number.isSafeInteger(range[0]) && + Number.isSafeInteger(range[1]) + ); +} + +/** + * @param range1 One range to check. + * @param range2 Another range to check. + * @param `true` if both ranges have some overlap. This overlap may consist of + * a single point, i.e. `[2, 5)` and `[4, 8)` are considered overlapping. */ -const escapeRegExp = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +function hasOverlap(range1: Range, range2: Range): boolean { + return range1[1] > range2[0] && range2[1] > range1[0]; +} /** - * Removes imports from original file - * @param code the whole file as text - * @param nodes to be removed + * Given two ranges that are known to overlap, constructs a new range + * representing the single range enclosing both ranges. + * @param range1 One range to process. + * @param range2 Another range to process. + * @returns A single range representing the union of both ranges. + */ +function mergeOverlappingRanges(range1: Range, range2: Range): Range { + return [Math.min(range1[0], range2[0]), Math.max(range1[1], range2[1])]; +} + +/** + * Given a list of ordered, possibly overlapping (non-disjoint) ranges, + * constructs a new list of ranges that consists entirely of disjoint ranges. + * The new list is also ordered. + * @param A list of ranges that may be overlapping, but are ordered by their + * start position. + * @return A list of disjoint ranges that are also ordered by their start + * position. + */ +function mergeRanges(ranges: Range[]): Range[] { + // Start with a result list initialized to the empty list + // Iterate over all given ranges. If a range overlaps the last item in + // the result list, replace the last item with the merger between that item + // and the range. Otherwise, just add the item to the result list. + // For comparison, see also + // https://www.geeksforgeeks.org/merging-intervals/ + const merged: Range[] = []; + for (const range of ranges) { + const currRange = merged[merged.length - 1]; + if (currRange !== undefined && hasOverlap(currRange, range)) { + merged[merged.length - 1] = mergeOverlappingRanges( + currRange, + range, + ); + } else { + merged.push(range); + } + } + return merged; +} + +/** + * Takes a list of ordered, disjoint, non-overlapping ranges and a range + * `[0, totalLength)` that encloses all those ranges. + * + * Constructs a new list of ranges representing the negation of the ranges with + * respect to the enclosing range `[0, totalLength)`. Put in other words, + * subtracts the given ranges from the range `[0, totalLength)`. + * + * More formally, let `r_1`, `r_2`, ..., `r_n` denote the sets represented by + * the given ranges; and let `r` be the set `[0, totalLength)`. Then this + * function returns a list of ranges representing the set + * + * > r \ r_1 \ r_2 \ ... \ r_n + * + * (where `\` is the set negation operator) + * @param ranges A list of disjoint (non-overlapping) ranges ordered by + * their start position. + * @param totalLength The end of the enclosing range from which to subtract + * the given ranges. + * @returns A list of ranges representing the inverse of the given ranges with + * respect to the enclosing range. + */ +function invertRanges(ranges: Range[], totalLength: number): Range[] { + // We'd run into out-of-bounds checks if we performed the rest of the + // algorithm with an empty array, and would have to insert additional + // checks. So just return immediately to keep the code simpler. + if (ranges.length === 0) { + return ranges; + } + + const resultRanges: Range[] = []; + const isValidRange = (start: number, end: number) => end > start; + + // Add the part from the start of the enclosing range to the start of the + // first range to exclude. + // + // |-----------xxxxx-----xxxx-----xxxx-----------| + // ^---------^ + // This part + const firstRange = ranges[0]; + if (isValidRange(0, firstRange[0])) { + resultRanges.push([0, firstRange[0]]); + } + + // Iterate over the parts between the ranges to remove and add those parts. + // + // |----------xxxxx-----xxxx------xxxx-----------| + // ^---^ ^----^ + // These parts + for (let index = 0; index < ranges.length - 1; index += 1) { + const prevRange = ranges[index]; + const nextRange = ranges[index + 1]; + const start = prevRange[1]; + const end = nextRange[0]; + if (isValidRange(start, end)) { + resultRanges.push([start, end]); + } + } + + // Add the part from the end of the last range to exclude to the end of the + // enclosing range. + // + // |----------xxxxx-----xxxx-----xxxx------------| + // ^----------^ + // This part + const lastRange = ranges[ranges.length - 1]; + if (isValidRange(lastRange[1], totalLength)) { + resultRanges.push([lastRange[1], totalLength]); + } + + return resultRanges; +} + +/** + * Given a piece of code and a list of nodes that appear in that code, removes + * all those nodes from the code. + * @param code The whole file as text + * @param nodes List of nodes to be removed from the code. + * @return The given code with all parts of the code removed that correspond to + * one of the given nodes. */ export const removeNodesFromOriginalCode = ( code: string, @@ -24,23 +169,27 @@ export const removeNodesFromOriginalCode = ( | CommentLine | ImportDeclaration | InterpreterDirective + | Directive )[], -) => { - let text = code; - for (const node of nodes) { - const start = Number(node.start); - const end = Number(node.end); - if (Number.isSafeInteger(start) && Number.isSafeInteger(end)) { - text = text.replace( - // only replace imports at the beginning of the line (ignoring whitespace) - // otherwise matching commented imports will be replaced - new RegExp( - '^\\s*' + escapeRegExp(code.substring(start, end)), - 'm', - ), - '', - ); - } - } - return text; +): string => { + // A list of ranges we should remove from the code + // Each range [start, end] consists of a start position in the code + // (inclusive) and an end position in the code (exclusive) + const excludes = nodes + .map((node) => [node.start, node.end] as const) + .filter(hasRange) + .sort(compareRangesByStart); + + // In case there are overlapping ranges, merge all overlapping ranges into + // a single range. + const mergedExcludes = mergeRanges(excludes); + + // Find the code ranges we want to keep by inverting the excludes with + // respect to the entire range [0, code.length] + const includes = invertRanges(mergedExcludes, code.length); + + // Extract all code parts we want to keep and join them together again + return includes + .map((include) => code.substring(include[0], include[1])) + .join(''); }; diff --git a/tests/PreserveStrict/__snapshots__/ppsi.spec.js.snap b/tests/PreserveStrict/__snapshots__/ppsi.spec.js.snap new file mode 100644 index 0000000..65bbed7 --- /dev/null +++ b/tests/PreserveStrict/__snapshots__/ppsi.spec.js.snap @@ -0,0 +1,132 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`comments-on-directive-are-preserved.ts - typescript-verify: comments-on-directive-are-preserved.ts 1`] = ` +// Comment above +/* foo + bar */ "use strict" ; +// Comment below +"another directive"; + +import './SetupEnvironment'; +import type { Period } from './Period' + +"this is not a directive prologue"; + +function foo() { +}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Comment above + +/* foo + bar */ +"use strict"; // Comment below + +"another directive"; + +import "./SetupEnvironment"; + +import type { Period } from "./Period"; + +("this is not a directive prologue"); + +function foo() {} + +`; + +exports[`custom-directive-remains-at-top.ts - typescript-verify: custom-directive-remains-at-top.ts 1`] = ` +// below is a directive prologue +'use custom' + +import './SetupEnvironment'; +import type { Period } from './Period' + +"this is not a directive prologue"; + +function foo() { +}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// below is a directive prologue +"use custom"; + +import "./SetupEnvironment"; + +import type { Period } from "./Period"; + +("this is not a directive prologue"); + +function foo() {} + +`; + +exports[`multiple-directives-remain-at-top.ts - typescript-verify: multiple-directives-remain-at-top.ts 1`] = ` +// below is a directive prologue + 'use custom' ; /* more directives... */ 'enable typecheck' + 'forbid IE' + +import './SetupEnvironment'; +import type { Period } from './Period' + +"this is not a directive prologue"; + +function foo() { +}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// below is a directive prologue +"use custom"; +/* more directives... */ + +"enable typecheck"; +"forbid IE"; + +import "./SetupEnvironment"; + +import type { Period } from "./Period"; + +("this is not a directive prologue"); + +function foo() {} + +`; + +exports[`strict-directive-remains-at-top.ts - typescript-verify: strict-directive-remains-at-top.ts 1`] = ` +// below is a directive prologue +'use strict' + +import './SetupEnvironment'; +import type { Period } from './Period' + +"this is not a directive prologue"; + +function foo() { +}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// below is a directive prologue +"use strict"; + +import "./SetupEnvironment"; + +import type { Period } from "./Period"; + +("this is not a directive prologue"); + +function foo() {} + +`; + +exports[`string-literal-at-top-is-pushed-down.ts - typescript-verify: string-literal-at-top-is-pushed-down.ts 1`] = ` +; 'use strict' + +import './SetupEnvironment'; +import type { Period } from './Period' + +"this is not a directive prologue"; + +function foo() { +}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import "./SetupEnvironment"; + +import type { Period } from "./Period"; + +("use strict"); + +("this is not a directive prologue"); + +function foo() {} + +`; diff --git a/tests/PreserveStrict/comments-on-directive-are-preserved.ts b/tests/PreserveStrict/comments-on-directive-are-preserved.ts new file mode 100644 index 0000000..b3b0421 --- /dev/null +++ b/tests/PreserveStrict/comments-on-directive-are-preserved.ts @@ -0,0 +1,13 @@ +// Comment above +/* foo + bar */ "use strict" ; +// Comment below +"another directive"; + +import './SetupEnvironment'; +import type { Period } from './Period' + +"this is not a directive prologue"; + +function foo() { +} \ No newline at end of file diff --git a/tests/PreserveStrict/custom-directive-remains-at-top.ts b/tests/PreserveStrict/custom-directive-remains-at-top.ts new file mode 100644 index 0000000..d4140d6 --- /dev/null +++ b/tests/PreserveStrict/custom-directive-remains-at-top.ts @@ -0,0 +1,10 @@ +// below is a directive prologue +'use custom' + +import './SetupEnvironment'; +import type { Period } from './Period' + +"this is not a directive prologue"; + +function foo() { +} \ No newline at end of file diff --git a/tests/PreserveStrict/multiple-directives-remain-at-top.ts b/tests/PreserveStrict/multiple-directives-remain-at-top.ts new file mode 100644 index 0000000..6d69ce6 --- /dev/null +++ b/tests/PreserveStrict/multiple-directives-remain-at-top.ts @@ -0,0 +1,11 @@ +// below is a directive prologue + 'use custom' ; /* more directives... */ 'enable typecheck' + 'forbid IE' + +import './SetupEnvironment'; +import type { Period } from './Period' + +"this is not a directive prologue"; + +function foo() { +} \ No newline at end of file diff --git a/tests/PreserveStrict/ppsi.spec.js b/tests/PreserveStrict/ppsi.spec.js new file mode 100644 index 0000000..617ccfe --- /dev/null +++ b/tests/PreserveStrict/ppsi.spec.js @@ -0,0 +1,5 @@ +run_spec(__dirname, ["typescript"], { + importOrder: ['^@core/(.*)$', '^@server/(.*)', '^@ui/(.*)$', '^[./]'], + importOrderSeparation: true, + importOrderParserPlugins : ['typescript', 'decorators-legacy', 'classProperties'] +}); diff --git a/tests/PreserveStrict/strict-directive-remains-at-top.ts b/tests/PreserveStrict/strict-directive-remains-at-top.ts new file mode 100644 index 0000000..c055e22 --- /dev/null +++ b/tests/PreserveStrict/strict-directive-remains-at-top.ts @@ -0,0 +1,10 @@ +// below is a directive prologue +'use strict' + +import './SetupEnvironment'; +import type { Period } from './Period' + +"this is not a directive prologue"; + +function foo() { +} \ No newline at end of file diff --git a/tests/PreserveStrict/string-literal-at-top-is-pushed-down.ts b/tests/PreserveStrict/string-literal-at-top-is-pushed-down.ts new file mode 100644 index 0000000..10a0eba --- /dev/null +++ b/tests/PreserveStrict/string-literal-at-top-is-pushed-down.ts @@ -0,0 +1,9 @@ +; 'use strict' + +import './SetupEnvironment'; +import type { Period } from './Period' + +"this is not a directive prologue"; + +function foo() { +} \ No newline at end of file diff --git a/tests/RemoveStatementsFromOriginalCode/__snapshots__/ppsi.spec.js.snap b/tests/RemoveStatementsFromOriginalCode/__snapshots__/ppsi.spec.js.snap new file mode 100644 index 0000000..10a22ed --- /dev/null +++ b/tests/RemoveStatementsFromOriginalCode/__snapshots__/ppsi.spec.js.snap @@ -0,0 +1,36 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`does-not-remove-content-in-string-literal.ts - typescript-verify: does-not-remove-content-in-string-literal.ts 1`] = ` +const exampleCode = \` + +import "./some-import"; + +\`; + +import "./some-import"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +import "./some-import"; + +const exampleCode = \` + +import "./some-import"; + +\`; + +`; + +exports[`does-remove-import-after-inline-comment.ts - typescript-verify: does-remove-import-after-inline-comment.ts 1`] = ` + +/* foo */ import { foo } from "c"; + +import { bar } from "a"; + +function baz() { +}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* foo */ +import { bar } from "a"; +import { foo } from "c"; + +function baz() {} + +`; diff --git a/tests/RemoveStatementsFromOriginalCode/does-not-remove-content-in-string-literal.ts b/tests/RemoveStatementsFromOriginalCode/does-not-remove-content-in-string-literal.ts new file mode 100644 index 0000000..926ee72 --- /dev/null +++ b/tests/RemoveStatementsFromOriginalCode/does-not-remove-content-in-string-literal.ts @@ -0,0 +1,7 @@ +const exampleCode = ` + +import "./some-import"; + +`; + +import "./some-import"; diff --git a/tests/RemoveStatementsFromOriginalCode/does-remove-import-after-inline-comment.ts b/tests/RemoveStatementsFromOriginalCode/does-remove-import-after-inline-comment.ts new file mode 100644 index 0000000..8c64bbe --- /dev/null +++ b/tests/RemoveStatementsFromOriginalCode/does-remove-import-after-inline-comment.ts @@ -0,0 +1,7 @@ + +/* foo */ import { foo } from "c"; + +import { bar } from "a"; + +function baz() { +} \ No newline at end of file diff --git a/tests/RemoveStatementsFromOriginalCode/ppsi.spec.js b/tests/RemoveStatementsFromOriginalCode/ppsi.spec.js new file mode 100644 index 0000000..617ccfe --- /dev/null +++ b/tests/RemoveStatementsFromOriginalCode/ppsi.spec.js @@ -0,0 +1,5 @@ +run_spec(__dirname, ["typescript"], { + importOrder: ['^@core/(.*)$', '^@server/(.*)', '^@ui/(.*)$', '^[./]'], + importOrderSeparation: true, + importOrderParserPlugins : ['typescript', 'decorators-legacy', 'classProperties'] +});