mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
9.8 KiB
209 lines
9.8 KiB
"use strict";
|
|
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.dev/license
|
|
*/
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.transformJasmineToVitest = transformJasmineToVitest;
|
|
/**
|
|
* @fileoverview This is the main entry point for the Jasmine to Vitest transformation.
|
|
* It orchestrates the application of various AST transformers to convert Jasmine test
|
|
* syntax and APIs to their Vitest equivalents. It also handles import management,
|
|
* blank line preservation, and reporting of transformation details.
|
|
*/
|
|
const typescript_1 = __importDefault(require("../../third_party/github.com/Microsoft/TypeScript/lib/typescript"));
|
|
const jasmine_lifecycle_1 = require("./transformers/jasmine-lifecycle");
|
|
const jasmine_matcher_1 = require("./transformers/jasmine-matcher");
|
|
const jasmine_misc_1 = require("./transformers/jasmine-misc");
|
|
const jasmine_spy_1 = require("./transformers/jasmine-spy");
|
|
const jasmine_type_1 = require("./transformers/jasmine-type");
|
|
const ast_helpers_1 = require("./utils/ast-helpers");
|
|
/**
|
|
* A placeholder used to temporarily replace blank lines in the source code.
|
|
* This is necessary because TypeScript's printer removes blank lines by default.
|
|
*/
|
|
const BLANK_LINE_PLACEHOLDER = '// __PRESERVE_BLANK_LINE__';
|
|
/**
|
|
* Vitest function names that should be imported when using the --add-imports option.
|
|
*/
|
|
const VITEST_FUNCTION_NAMES = new Set([
|
|
'describe',
|
|
'it',
|
|
'expect',
|
|
'beforeEach',
|
|
'afterEach',
|
|
'beforeAll',
|
|
'afterAll',
|
|
]);
|
|
/**
|
|
* Replaces blank lines in the content with a placeholder to prevent TypeScript's printer
|
|
* from removing them. This ensures that the original formatting of blank lines is preserved.
|
|
* @param content The source code content.
|
|
* @returns The content with blank lines replaced by placeholders.
|
|
*/
|
|
function preserveBlankLines(content) {
|
|
return content
|
|
.split('\n')
|
|
.map((line) => (line.trim() === '' ? BLANK_LINE_PLACEHOLDER : line))
|
|
.join('\n');
|
|
}
|
|
/**
|
|
* Restores blank lines in the content by replacing the placeholder with actual blank lines.
|
|
* This is called after TypeScript's printer has processed the file.
|
|
* @param content The content with blank line placeholders.
|
|
* @returns The content with blank lines restored.
|
|
*/
|
|
function restoreBlankLines(content) {
|
|
const regex = /^\s*\/\/ __PRESERVE_BLANK_LINE__\s*$/gm;
|
|
return content.replace(regex, '');
|
|
}
|
|
/**
|
|
* A collection of transformers that operate on `ts.CallExpression` nodes.
|
|
* These are applied in stages to ensure correct order of operations:
|
|
* 1. High-Level & Context-Sensitive: Transformations that fundamentally change the call.
|
|
* 2. Core Matcher & Spy: Bulk conversions for `expect(...)` and `spyOn(...)`.
|
|
* 3. Global Functions & Cleanup: Handles global Jasmine functions and unsupported APIs.
|
|
*/
|
|
const callExpressionTransformers = [
|
|
// **Stage 1: High-Level & Context-Sensitive Transformations**
|
|
// These transformers often wrap or fundamentally change the nature of the call,
|
|
// so they need to run before more specific matchers.
|
|
jasmine_matcher_1.transformWithContext,
|
|
jasmine_matcher_1.transformExpectAsync,
|
|
jasmine_lifecycle_1.transformFocusedAndSkippedTests,
|
|
jasmine_lifecycle_1.transformPending,
|
|
jasmine_lifecycle_1.transformDoneCallback,
|
|
// **Stage 2: Core Matcher & Spy Transformations**
|
|
// This is the bulk of the `expect(...)` and `spyOn(...)` conversions.
|
|
jasmine_matcher_1.transformSyntacticSugarMatchers,
|
|
jasmine_matcher_1.transformComplexMatchers,
|
|
jasmine_spy_1.transformSpies,
|
|
jasmine_spy_1.transformCreateSpyObj,
|
|
jasmine_spy_1.transformSpyReset,
|
|
jasmine_spy_1.transformSpyCallInspection,
|
|
jasmine_matcher_1.transformtoHaveBeenCalledBefore,
|
|
jasmine_matcher_1.transformToHaveClass,
|
|
// **Stage 3: Global Functions & Cleanup**
|
|
// These handle global Jasmine functions and catch-alls for unsupported APIs.
|
|
jasmine_misc_1.transformTimerMocks,
|
|
jasmine_misc_1.transformGlobalFunctions,
|
|
jasmine_misc_1.transformUnsupportedJasmineCalls,
|
|
];
|
|
/**
|
|
* A collection of transformers that operate on `ts.PropertyAccessExpression` nodes.
|
|
* These primarily handle `jasmine.any()` and other `jasmine.*` properties.
|
|
*/
|
|
const propertyAccessExpressionTransformers = [
|
|
// These transformers handle `jasmine.any()` and other `jasmine.*` properties.
|
|
jasmine_matcher_1.transformAsymmetricMatchers,
|
|
jasmine_spy_1.transformSpyCallInspection,
|
|
jasmine_misc_1.transformUnknownJasmineProperties,
|
|
];
|
|
/**
|
|
* A collection of transformers that operate on `ts.ExpressionStatement` nodes.
|
|
* These are mutually exclusive; the first one that matches will be applied.
|
|
*/
|
|
const expressionStatementTransformers = [
|
|
jasmine_matcher_1.transformCalledOnceWith,
|
|
jasmine_matcher_1.transformArrayWithExactContents,
|
|
jasmine_matcher_1.transformExpectNothing,
|
|
jasmine_misc_1.transformFail,
|
|
jasmine_misc_1.transformDefaultTimeoutInterval,
|
|
];
|
|
/**
|
|
* Transforms a string of Jasmine test code to Vitest test code.
|
|
* This is the main entry point for the transformation.
|
|
* @param filePath The path to the file being transformed.
|
|
* @param content The source code to transform.
|
|
* @param reporter The reporter to track TODOs.
|
|
* @param options Transformation options, including whether to add Vitest API imports.
|
|
* @returns The transformed code.
|
|
*/
|
|
function transformJasmineToVitest(filePath, content, reporter, options) {
|
|
const contentWithPlaceholders = preserveBlankLines(content);
|
|
const sourceFile = typescript_1.default.createSourceFile(filePath, contentWithPlaceholders, typescript_1.default.ScriptTarget.Latest, true, typescript_1.default.ScriptKind.TS);
|
|
const pendingVitestValueImports = new Set();
|
|
const pendingVitestTypeImports = new Set();
|
|
const transformer = (context) => {
|
|
const refactorCtx = {
|
|
sourceFile,
|
|
reporter,
|
|
tsContext: context,
|
|
pendingVitestValueImports,
|
|
pendingVitestTypeImports,
|
|
};
|
|
const visitor = (node) => {
|
|
let transformedNode = node;
|
|
// Transform the node itself based on its type
|
|
if (typescript_1.default.isCallExpression(transformedNode)) {
|
|
if (options.addImports && typescript_1.default.isIdentifier(transformedNode.expression)) {
|
|
const name = transformedNode.expression.text;
|
|
if (VITEST_FUNCTION_NAMES.has(name)) {
|
|
(0, ast_helpers_1.addVitestValueImport)(pendingVitestValueImports, name);
|
|
}
|
|
}
|
|
for (const transformer of callExpressionTransformers) {
|
|
if (!(options.browserMode && transformer === jasmine_matcher_1.transformToHaveClass)) {
|
|
transformedNode = transformer(transformedNode, refactorCtx);
|
|
}
|
|
}
|
|
}
|
|
else if (typescript_1.default.isPropertyAccessExpression(transformedNode)) {
|
|
for (const transformer of propertyAccessExpressionTransformers) {
|
|
transformedNode = transformer(transformedNode, refactorCtx);
|
|
}
|
|
}
|
|
else if (typescript_1.default.isExpressionStatement(transformedNode)) {
|
|
// Statement-level transformers are mutually exclusive. The first one that
|
|
// matches will be applied, and then the visitor will stop for this node.
|
|
for (const transformer of expressionStatementTransformers) {
|
|
const result = transformer(transformedNode, refactorCtx);
|
|
if (result !== transformedNode) {
|
|
transformedNode = result;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (typescript_1.default.isQualifiedName(transformedNode) || typescript_1.default.isTypeReferenceNode(transformedNode)) {
|
|
transformedNode = (0, jasmine_type_1.transformJasmineTypes)(transformedNode, refactorCtx);
|
|
}
|
|
// Visit the children of the node to ensure they are transformed
|
|
if (Array.isArray(transformedNode)) {
|
|
return transformedNode.map((node) => typescript_1.default.visitEachChild(node, visitor, context));
|
|
}
|
|
else {
|
|
return typescript_1.default.visitEachChild(transformedNode, visitor, context);
|
|
}
|
|
};
|
|
return (node) => typescript_1.default.visitEachChild(node, visitor, context);
|
|
};
|
|
const result = typescript_1.default.transform(sourceFile, [transformer]);
|
|
let transformedSourceFile = result.transformed[0];
|
|
const hasPendingValueImports = pendingVitestValueImports.size > 0;
|
|
const hasPendingTypeImports = pendingVitestTypeImports.size > 0;
|
|
if (transformedSourceFile === sourceFile &&
|
|
!reporter.hasTodos &&
|
|
!hasPendingValueImports &&
|
|
!hasPendingTypeImports) {
|
|
return content;
|
|
}
|
|
if (hasPendingTypeImports || (options.addImports && hasPendingValueImports)) {
|
|
const vitestImport = (0, ast_helpers_1.getVitestAutoImports)(options.addImports ? pendingVitestValueImports : new Set(), pendingVitestTypeImports);
|
|
if (vitestImport) {
|
|
transformedSourceFile = typescript_1.default.factory.updateSourceFile(transformedSourceFile, [
|
|
vitestImport,
|
|
...transformedSourceFile.statements,
|
|
]);
|
|
}
|
|
}
|
|
const printer = typescript_1.default.createPrinter();
|
|
const transformedContentWithPlaceholders = printer.printFile(transformedSourceFile);
|
|
return restoreBlankLines(transformedContentWithPlaceholders);
|
|
}
|
|
//# sourceMappingURL=test-file-transformer.js.map
|