Skip to content

Commit

Permalink
Improve import-sort-parser-babylon
Browse files Browse the repository at this point in the history
- Read .babelrc (or similar) if file is passed to import-sort
- Parse file as TypeScript if given file has .ts or .tsx extension
  • Loading branch information
renke committed Feb 2, 2019
1 parent bb699a9 commit c782850
Show file tree
Hide file tree
Showing 14 changed files with 549 additions and 103 deletions.
3 changes: 3 additions & 0 deletions packages/import-sort-parser-babylon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
"lib"
],
"devDependencies": {
"@babel/preset-flow": "^7.0.0",
"@babel/preset-typescript": "^7.1.0",
"@types/babylon": "^6.16.3",
"@types/chai": "^4.1.4",
"@types/mocha": "^5.2.5",
Expand All @@ -31,6 +33,7 @@
"typescript": "^3.2.4"
},
"dependencies": {
"@babel/core": "^7.2.2",
"@babel/parser": "^7.0.0-beta.54",
"@babel/traverse": "^7.0.0-beta.54",
"@babel/types": "^7.0.0-beta.54",
Expand Down
65 changes: 56 additions & 9 deletions packages/import-sort-parser-babylon/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
import {parse} from "@babel/parser";
import {extname} from "path";
import {ParseOptions} from "querystring";

import {
loadOptions as babelLoadOptions,
loadPartialConfig as babelLoadPartialOptions,
parse as babelParse,
} from "@babel/core";
import {
ParserOptions,
ParserPlugin,
parse as babelParserParse,
} from "@babel/parser";
import traverse from "@babel/traverse";
import {
isImportDefaultSpecifier,
isImportNamespaceSpecifier,
isImportSpecifier,
} from "@babel/types";
// tslint:disable-next-line:no-implicit-dependencies
import {IImport, NamedMember} from "import-sort-parser";
import {IImport, IParserOptions, NamedMember} from "import-sort-parser";

// TODO: Mocha currently doesn't pick up the declaration in index.d.ts
// tslint:disable-next-line:no-var-requires
const findLineColumn = require("find-line-column");

const BABYLON_PLUGINS = [
const TYPESCRIPT_EXTENSIONS = [".ts", ".tsx"];

const COMMON_PARSER_PLUGINS = [
"jsx",
"flow",
"flowComments",
"doExpressions",
"objectRestSpread",
["decorators", {decoratorsBeforeExport: true}],
Expand All @@ -38,19 +50,54 @@ const BABYLON_PLUGINS = [
"nullishCoalescingOperator",
];

const BABYLON_OPTIONS = {
const FLOW_PARSER_PLUGINS = ["flow", "flowComments", ...COMMON_PARSER_PLUGINS];

const FLOW_PARSER_OPTIONS = {
allowImportExportEverywhere: true,
allowAwaitOutsideFunction: true,
allowReturnOutsideFunction: true,
allowSuperOutsideMethod: true,

sourceType: "module",

plugins: BABYLON_PLUGINS,
plugins: FLOW_PARSER_PLUGINS,
};

export function parseImports(code: string): Array<IImport> {
const parsed = (parse as any)(code, BABYLON_OPTIONS);
const TYPESCRIPT_PARSER_PLUGINS = ["typescript", ...COMMON_PARSER_PLUGINS];

const TYPESCRIPT_PARSER_OPTIONS = {
allowImportExportEverywhere: true,
allowAwaitOutsideFunction: true,
allowReturnOutsideFunction: true,
allowSuperOutsideMethod: true,

sourceType: "module",

plugins: TYPESCRIPT_PARSER_PLUGINS,
};

export function parseImports(
code: string,
options: IParserOptions = {},
): Array<IImport> {
const babelPartialOptions = babelLoadPartialOptions({filename: options.file});

let parsed;

if (babelPartialOptions.hasFilesystemConfig()) {
// We always prefer .babelrc (or similar) if one was found
parsed = babelParse(code, babelLoadOptions({filename: options.file}));
} else {
const {file} = options;

const isTypeScript = file && TYPESCRIPT_EXTENSIONS.includes(extname(file));

const parserOptions = isTypeScript
? TYPESCRIPT_PARSER_OPTIONS
: FLOW_PARSER_OPTIONS;

parsed = babelParserParse(code, parserOptions as any);
}

const imports: Array<IImport> = [];

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-flow"]
}
37 changes: 37 additions & 0 deletions packages/import-sort-parser-babylon/test/flow-babelrc/flow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import "mocha";

import {assert} from "chai";
import {IImport} from "import-sort-parser";

import {formatImport, parseImports} from "../../lib";

const parseFlowImports = code => {
// Pass (fake) file name to the parser so it can read .babelrc
return parseImports(code, {file: __dirname + "/flow.js"});
};

describe("parseImports (Flow, with @babel/preset-flow)", () => {
it("should return default type import", () => {
const imports = parseFlowImports(
`
import type p from 'q';
`.trim(),
);

assert.equal(imports[0].type, "import-type");
assert.equal(imports[0].start, 0);
assert.equal(imports[0].end, imports[0].end);
assert.equal(imports[0].moduleName, "q");
assert.equal(imports[0].defaultMember, "p");
});

it("should include type information for named type imports", () => {
const imports = parseFlowImports(
`
import {type a} from "x";
`.trim(),
);

assert.equal(imports[0].namedMembers[0].type, true);
});
});
37 changes: 37 additions & 0 deletions packages/import-sort-parser-babylon/test/flow/flow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import "mocha";

import {assert} from "chai";
import {IImport} from "import-sort-parser";

import {formatImport, parseImports} from "../../lib";

const parseFlowImports = code => {
// No file name is passed to the parser here
return parseImports(code);
};

describe("parseImports (Flow, without @babel/preset-flow)", () => {
it("should return default type import", () => {
const imports = parseFlowImports(
`
import type p from 'q';
`.trim(),
);

assert.equal(imports[0].type, "import-type");
assert.equal(imports[0].start, 0);
assert.equal(imports[0].end, imports[0].end);
assert.equal(imports[0].moduleName, "q");
assert.equal(imports[0].defaultMember, "p");
});

it("should include type information for named type imports", () => {
const imports = parseFlowImports(
`
import {type a} from "x";
`.trim(),
);

assert.equal(imports[0].namedMembers[0].type, true);
});
});
14 changes: 0 additions & 14 deletions packages/import-sort-parser-babylon/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,6 @@ import l, * as m from "o";
assert.equal(imports[6].namespaceMember, "m");
});

it("should return default type import", () => {
const imports = parseImports(
`
import type p from 'q';
`.trim(),
);

assert.equal(imports[0].type, "import-type");
assert.equal(imports[0].start, 0);
assert.equal(imports[0].end, imports[0].end);
assert.equal(imports[0].moduleName, "q");
assert.equal(imports[0].defaultMember, "p");
});

it("should include nearby comments", () => {
const imports = parseImports(
`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-typescript"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import "mocha";

import {assert} from "chai";

import {parseImports} from "../../lib";

const parseTypeScriptImports = code => {
// Pass (fake) file name to the parser so it can read .babelrc
return parseImports(code, {file: __dirname + "/typescript.ts"});
};

describe("parseImports (TypeScript, with @babel/preset-typescript)", () => {
it("should return imports", () => {
const imports = parseTypeScriptImports(
`
import "a";
import b from "b";
import {c} from "c";
import d, {e} from "f";
import g, {h as hh} from "i";
import * as j from "k";
import l, * as m from "o";
// Random TypeScript syntax (that is not Flow syntax)
const a: number = "123" as any;
`.trim(),
);

assert.equal(imports.length, 7);

imports.forEach(imported => {
assert.equal(imported.type, "import");
});

// import "a";
assert.equal(imports[0].start, 0);
assert.equal(imports[0].end, 11);
assert.equal(imports[0].moduleName, "a");

// import b from "b";
assert.equal(imports[1].start, imports[0].end + 1);
assert.equal(imports[1].end, imports[0].end + 1 + 18);
assert.equal(imports[1].moduleName, "b");
assert.equal(imports[1].defaultMember, "b");

// import {c} from "c";
assert.equal(imports[2].start, imports[1].end + 1);
assert.equal(imports[2].end, imports[1].end + 1 + 20);
assert.equal(imports[2].moduleName, "c");
assert.deepEqual(imports[2].namedMembers![0], {name: "c", alias: "c"});

// import d, {e} from "f";
assert.equal(imports[3].start, imports[2].end + 1);
assert.equal(imports[3].end, imports[2].end + 1 + 23);
assert.equal(imports[3].moduleName, "f");
assert.equal(imports[3].defaultMember, "d");
assert.deepEqual(imports[3].namedMembers![0], {name: "e", alias: "e"});

// import g, {h as hh} from "i";
assert.equal(imports[4].start, imports[3].end + 1);
assert.equal(imports[4].end, imports[3].end + 1 + 29);
assert.equal(imports[4].moduleName, "i");
assert.equal(imports[4].defaultMember, "g");
assert.deepEqual(imports[4].namedMembers![0], {name: "h", alias: "hh"});

// import * as j from "k";
assert.equal(imports[5].start, imports[4].end + 1);
assert.equal(imports[5].end, imports[4].end + 1 + 23);
assert.equal(imports[5].moduleName, "k");
assert.equal(imports[5].namespaceMember, "j");

// import l, * as m from "o";
assert.equal(imports[6].start, imports[5].end + 1);
assert.equal(imports[6].end, imports[5].end + 1 + 26);
assert.equal(imports[6].moduleName, "o");
assert.equal(imports[6].defaultMember, "l");
assert.equal(imports[6].namespaceMember, "m");
});
});
79 changes: 79 additions & 0 deletions packages/import-sort-parser-babylon/test/typescript/typescript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import "mocha";

import {assert} from "chai";

import {parseImports} from "../../lib";

const parseTypeScriptImports = code => {
// No file name is passed to the parser here
return parseImports(code, {file: __dirname + "/typescript.ts"});
};

describe("parseImports (TypeScript, without @babel/preset-typescript)", () => {
it("should return imports", () => {
const imports = parseTypeScriptImports(
`
import "a";
import b from "b";
import {c} from "c";
import d, {e} from "f";
import g, {h as hh} from "i";
import * as j from "k";
import l, * as m from "o";
// Random TypeScript syntax (that is not Flow syntax)
const a: number = "123" as any;
`.trim(),
);

assert.equal(imports.length, 7);

imports.forEach(imported => {
assert.equal(imported.type, "import");
});

// import "a";
assert.equal(imports[0].start, 0);
assert.equal(imports[0].end, 11);
assert.equal(imports[0].moduleName, "a");

// import b from "b";
assert.equal(imports[1].start, imports[0].end + 1);
assert.equal(imports[1].end, imports[0].end + 1 + 18);
assert.equal(imports[1].moduleName, "b");
assert.equal(imports[1].defaultMember, "b");

// import {c} from "c";
assert.equal(imports[2].start, imports[1].end + 1);
assert.equal(imports[2].end, imports[1].end + 1 + 20);
assert.equal(imports[2].moduleName, "c");
assert.deepEqual(imports[2].namedMembers![0], {name: "c", alias: "c"});

// import d, {e} from "f";
assert.equal(imports[3].start, imports[2].end + 1);
assert.equal(imports[3].end, imports[2].end + 1 + 23);
assert.equal(imports[3].moduleName, "f");
assert.equal(imports[3].defaultMember, "d");
assert.deepEqual(imports[3].namedMembers![0], {name: "e", alias: "e"});

// import g, {h as hh} from "i";
assert.equal(imports[4].start, imports[3].end + 1);
assert.equal(imports[4].end, imports[3].end + 1 + 29);
assert.equal(imports[4].moduleName, "i");
assert.equal(imports[4].defaultMember, "g");
assert.deepEqual(imports[4].namedMembers![0], {name: "h", alias: "hh"});

// import * as j from "k";
assert.equal(imports[5].start, imports[4].end + 1);
assert.equal(imports[5].end, imports[4].end + 1 + 23);
assert.equal(imports[5].moduleName, "k");
assert.equal(imports[5].namespaceMember, "j");

// import l, * as m from "o";
assert.equal(imports[6].start, imports[5].end + 1);
assert.equal(imports[6].end, imports[5].end + 1 + 26);
assert.equal(imports[6].moduleName, "o");
assert.equal(imports[6].defaultMember, "l");
assert.equal(imports[6].namespaceMember, "m");
});
});
6 changes: 3 additions & 3 deletions packages/import-sort-parser/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
export interface IParseOptions {
filename?: string;
export interface IParserOptions {
file?: string;
}

export interface IParser {
parseImports(code: string, options?: IParseOptions): Array<IImport>;
parseImports(code: string, options?: IParserOptions): Array<IImport>;
formatImport(code: string, imported: IImport, eol?: string): string;
}

Expand Down
Loading

0 comments on commit c782850

Please sign in to comment.