Skip to content

Commit

Permalink
fix: use decorator-legacy on ts file
Browse files Browse the repository at this point in the history
  • Loading branch information
zxch3n committed May 1, 2022
1 parent 6d16a9e commit c04d4b9
Show file tree
Hide file tree
Showing 6 changed files with 322 additions and 105 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
"prettier": "^2.5.1",
"typescript": "^4.5.5",
"vite": "^2.8.6",
"vitest": "^0.8.0"
"vitest": "latest"
},
"dependencies": {
"@babel/parser": "^7.17.3",
Expand Down
30 changes: 16 additions & 14 deletions samples/basic/test/add.test.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import { describe, expect, it } from "vitest";

describe("addition", () => {
it("run", () => {
console.log("=================");
console.log("Console Output");
expect(1 + 1).toBe(2);
});
describe("haha", () => {
it("run", () => {
console.log("=================");
console.log("Console Output");
expect(1 + 1).toBe(2);
});

it("should failed", async () => {
await new Promise((r) => setTimeout(r, 100));
expect(1 + 2).toBe(2);
});
it("should failed", async () => {
await new Promise((r) => setTimeout(r, 100));
expect(1 + 2).toBe(2);
});

it.skip("skipped", () => {
expect(1 + 1).toBe(3);
});
it.skip("skipped", () => {
expect(1 + 1).toBe(3);
});

it.todo("todo");
it("same name", () => {});
it.todo("todo");
it("same name", () => {});
});
});

describe("testing", () => {
Expand Down
100 changes: 63 additions & 37 deletions src/pure/parsers/babel_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,25 @@
* @flow
*/

import { readFileSync } from 'fs';
import { File as BabelFile, Node as BabelNode, Statement } from '@babel/types';
import * as parser from '@babel/parser';
import type { ParsedNodeType } from './parser_nodes';
import { NamedBlock, ParsedRange, ParseResult, ParsedNode } from './parser_nodes';
import { parseOptions } from './helper';

const _getASTfor = (file: string, data?: string, options?: parser.ParserOptions): [BabelFile, string] => {
import { readFileSync } from "fs";
import { File as BabelFile, Node as BabelNode, Statement } from "@babel/types";
import * as parser from "@babel/parser";
import type { ParsedNodeType } from "./parser_nodes";
import {
NamedBlock,
ParsedNode,
ParsedRange,
ParseResult,
} from "./parser_nodes";
import { parseOptions } from "./helper";

const _getASTfor = (
file: string,
data?: string,
options?: parser.ParserOptions,
): [BabelFile, string] => {
const _data = data || readFileSync(file).toString();
const config = { ...options, sourceType: 'module' as const };
const config = { ...options, sourceType: "module" as const };
return [parser.parse(_data, config), _data];
};

Expand All @@ -28,12 +37,16 @@ export const getASTfor = (file: string, data?: string): BabelFile => {
};

export function doesImportVitest(ast: BabelFile): boolean {
return ast.program.body.some(x => {
return x.type === 'ImportDeclaration' && x.source.value === 'vitest';
return ast.program.body.some((x) => {
return x.type === "ImportDeclaration" && x.source.value === "vitest";
});
}

export const parse = (file: string, data?: string, options?: parser.ParserOptions): ParseResult => {
export const parse = (
file: string,
data?: string,
options?: parser.ParserOptions,
): ParseResult => {
const parseResult = new ParseResult(file);
const [ast, _data] = _getASTfor(file, data, options);

Expand All @@ -47,14 +60,18 @@ export const parse = (file: string, data?: string, options?: parser.ParserOption
return rootForType;
}, node);

const updateNameInfo = (nBlock: NamedBlock, bNode: BabelNode, lastProperty?: string) => {
const updateNameInfo = (
nBlock: NamedBlock,
bNode: BabelNode,
lastProperty?: string,
) => {
//@ts-ignore
const arg = bNode.expression.arguments[0];
let name = arg.value;

if (!name) {
switch (arg.type) {
case 'TemplateLiteral':
case "TemplateLiteral":
name = _data.substring(arg.start + 1, arg.end - 1);
break;
default:
Expand All @@ -70,11 +87,15 @@ export const parse = (file: string, data?: string, options?: parser.ParserOption
arg.loc.start.line,
arg.loc.start.column + 2,
arg.loc.end.line,
arg.loc.end.column - 1
arg.loc.end.column - 1,
);
};

const updateNode = (node: ParsedNode, babylonNode: BabelNode, lastProperty?: string) => {
const updateNode = (
node: ParsedNode,
babylonNode: BabelNode,
lastProperty?: string,
) => {
//@ts-ignore
node.start = babylonNode.loc.start;
//@ts-ignore
Expand All @@ -88,23 +109,24 @@ export const parse = (file: string, data?: string, options?: parser.ParserOption
};

const isFunctionCall = (node: BabelNode) =>
node && node.type === 'ExpressionStatement' && node.expression && node.expression.type === 'CallExpression';
node && node.type === "ExpressionStatement" && node.expression &&
node.expression.type === "CallExpression";

const isFunctionDeclaration = (nodeType: string) =>
nodeType === 'ArrowFunctionExpression' || nodeType === 'FunctionExpression';
nodeType === "ArrowFunctionExpression" || nodeType === "FunctionExpression";

// Pull out the name of a CallExpression (describe/it) and the last property (each, skip etc)
const getNameForNode = (node: any) => {
if (isFunctionCall(node) && node.expression.callee) {
// Get root callee in case it's a chain of higher-order functions (e.g. .each(table)(name, fn))
const rootCallee = deepGet(node.expression, 'callee');
const property = rootCallee.property?.name || deepGet(rootCallee, 'tag').property?.name;
const name =
rootCallee.name ||
const rootCallee = deepGet(node.expression, "callee");
const property = rootCallee.property?.name ||
deepGet(rootCallee, "tag").property?.name;
const name = rootCallee.name ||
// handle cases where it's a member expression (e.g .only or .concurrent.only)
deepGet(rootCallee, 'object').name ||
deepGet(rootCallee, "object").name ||
// handle cases where it's part of a tag (e.g. .each`table`)
deepGet(rootCallee, 'tag', 'object').name;
deepGet(rootCallee, "tag", "object").name;

return [name, property];
}
Expand All @@ -114,11 +136,11 @@ export const parse = (file: string, data?: string, options?: parser.ParserOption
// When given a node in the AST, does this represent
// the start of an it/test block?
const isAnIt = (name?: string) => {
return name === 'it' || name === 'fit' || name === 'test';
return name === "it" || name === "fit" || name === "test";
};

const isAnDescribe = (name?: string) => {
return name === 'describe';
return name === "describe";
};

// When given a node in the AST, does this represent
Expand All @@ -127,7 +149,7 @@ export const parse = (file: string, data?: string, options?: parser.ParserOption
if (!isFunctionCall(node)) {
return false;
}
let name = '';
let name = "";
let element = node && node.expression ? node.expression.callee : undefined;
while (!name && element) {
// eslint-disable-next-line prefer-destructuring
Expand All @@ -136,14 +158,14 @@ export const parse = (file: string, data?: string, options?: parser.ParserOption
// (expect()) we have to check multiple levels for the name
element = element.object || element.callee;
}
return name === 'expect';
return name === "expect";
};

const addNode = (
type: ParsedNodeType,
parent: ParsedNode,
babylonNode: BabelNode,
lastProperty?: string
lastProperty?: string,
): ParsedNode => {
const child = parent.addChild(type);
updateNode(child, babylonNode, lastProperty);
Expand Down Expand Up @@ -171,25 +193,29 @@ export const parse = (file: string, data?: string, options?: parser.ParserOption

const [name, lastProperty] = getNameForNode(element);
if (isAnDescribe(name)) {
child = addNode('describe', parent, element, lastProperty);
child = addNode("describe", parent, element, lastProperty);
} else if (isAnIt(name)) {
child = addNode('it', parent, element, lastProperty);
child = addNode("it", parent, element, lastProperty);
} else if (isAnExpect(element)) {
child = addNode('expect', parent, element);
} else if (element && element.type === 'VariableDeclaration') {
child = addNode("expect", parent, element);
} else if (element && element.type === "VariableDeclaration") {
element.declarations
.filter((declaration) => declaration.init && isFunctionDeclaration(declaration.init.type))
.filter((declaration) =>
declaration.init && isFunctionDeclaration(declaration.init.type)
)
.forEach((declaration) => searchNodes(declaration.init.body, parent));
} else if (
element &&
element.type === 'ExpressionStatement' &&
element.type === "ExpressionStatement" &&
element.expression &&
element.expression.type === 'AssignmentExpression' &&
element.expression.type === "AssignmentExpression" &&
element.expression.right &&
isFunctionDeclaration(element.expression.right.type)
) {
searchNodes(element.expression.right.body, parent);
} else if (element.type === 'ReturnStatement' && element.argument?.arguments) {
} else if (
element.type === "ReturnStatement" && element.argument?.arguments
) {
element.argument.arguments
.filter((argument) => isFunctionDeclaration(argument.type))
.forEach((argument) => searchNodes(argument.body, parent));
Expand Down
85 changes: 51 additions & 34 deletions src/pure/parsers/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,72 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import {ParserOptions, ParserPlugin} from '@babel/parser';
import { ParserOptions, ParserPlugin } from "@babel/parser";

const commonPlugins: ParserPlugin[] = [
'asyncGenerators',
'bigInt',
'classPrivateMethods',
'classPrivateProperties',
'classProperties',
'doExpressions',
'dynamicImport',
'estree',
'exportDefaultFrom',
'exportNamespaceFrom', // deprecated
'functionBind',
'functionSent',
'importMeta',
'logicalAssignment',
'nullishCoalescingOperator',
'numericSeparator',
'objectRestSpread',
'optionalCatchBinding',
'optionalChaining',
'partialApplication',
'throwExpressions',
'topLevelAwait',
['decorators', {decoratorsBeforeExport: true}],
['pipelineOperator', {proposal: 'smart'}],
"asyncGenerators",
"bigInt",
"classPrivateMethods",
"classPrivateProperties",
"classProperties",
"doExpressions",
"dynamicImport",
"estree",
"exportDefaultFrom",
"exportNamespaceFrom", // deprecated
"functionBind",
"functionSent",
"importMeta",
"logicalAssignment",
"nullishCoalescingOperator",
"numericSeparator",
"objectRestSpread",
"optionalCatchBinding",
"optionalChaining",
"partialApplication",
"throwExpressions",
"topLevelAwait",
["pipelineOperator", { proposal: "smart" }],
];

export const jsPlugins: ParserPlugin[] = [...commonPlugins, 'flow', 'jsx'];
export const tsPlugins: ParserPlugin[] = [...commonPlugins, 'typescript'];
export const tsxPlugins: ParserPlugin[] = [...commonPlugins, 'typescript', 'jsx'];
export const jsPlugins: ParserPlugin[] = [
...commonPlugins,
["decorators", { decoratorsBeforeExport: true }],
"flow",
"jsx",
];
export const tsPlugins: ParserPlugin[] = [
...commonPlugins,
"decorators-legacy",
"typescript",
];
export const tsxPlugins: ParserPlugin[] = [
...commonPlugins,
"decorators-legacy",
"typescript",
"jsx",
];

export const parseOptions = (filePath: string, strictMode = false): ParserOptions => {
export const parseOptions = (
filePath: string,
strictMode = false,
): ParserOptions => {
if (filePath.match(/\.ts$/i)) {
return {plugins: [...tsPlugins]};
return { plugins: [...tsPlugins] };
}

if (filePath.match(/\.tsx$/i)) {
return {plugins: [...tsxPlugins]};
return { plugins: [...tsxPlugins] };
}

// for backward compatibility, use js parser as default unless in strict mode
if (!strictMode || filePath.match(/\.m?jsx?$/i)) {
return {plugins: [...jsPlugins]};
return { plugins: [...jsPlugins] };
}

throw new TypeError(`unable to find parser options for unrecognized file extension: ${filePath}`);
throw new TypeError(
`unable to find parser options for unrecognized file extension: ${filePath}`,
);
};
Loading

0 comments on commit c04d4b9

Please sign in to comment.