From 7456f50478e80efe07135975a668f85ba582178d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Wed, 1 Jun 2022 08:57:28 -0400 Subject: [PATCH] Improve fixture-test-runner typings (#14625) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * plugin-test-runner * fixture-test-runner * Update packages/babel-helper-fixtures/src/index.ts Co-authored-by: Nicolò Ribaudo Co-authored-by: Nicolò Ribaudo --- packages/babel-core/src/index.ts | 7 +- packages/babel-core/src/transform.ts | 2 + .../babel-core/src/transformation/index.ts | 2 +- packages/babel-helper-fixtures/src/index.ts | 84 +++++++++++++------ .../src/index.ts | 2 +- .../src/helpers.ts | 4 +- .../src/index.ts | 63 +++++++++----- 7 files changed, 113 insertions(+), 51 deletions(-) diff --git a/packages/babel-core/src/index.ts b/packages/babel-core/src/index.ts index 81ea29c99e66..9698e3fd1a6f 100644 --- a/packages/babel-core/src/index.ts +++ b/packages/babel-core/src/index.ts @@ -40,7 +40,12 @@ export type { PresetObject, } from "./config"; -export { transform, transformSync, transformAsync } from "./transform"; +export { + transform, + transformSync, + transformAsync, + type FileResult, +} from "./transform"; export { transformFile, transformFileSync, diff --git a/packages/babel-core/src/transform.ts b/packages/babel-core/src/transform.ts index 668b20de5ab7..c44e62994a9d 100644 --- a/packages/babel-core/src/transform.ts +++ b/packages/babel-core/src/transform.ts @@ -6,6 +6,8 @@ import { run } from "./transformation"; import type { FileResult, FileResultCallback } from "./transformation"; +export type { FileResult } from "./transformation"; + type Transform = { (code: string, callback: FileResultCallback): void; ( diff --git a/packages/babel-core/src/transformation/index.ts b/packages/babel-core/src/transformation/index.ts index 4d83285e9735..b6d6875705b1 100644 --- a/packages/babel-core/src/transformation/index.ts +++ b/packages/babel-core/src/transformation/index.ts @@ -23,7 +23,7 @@ export type FileResultCallback = { export type FileResult = { metadata: {}; options: {}; - ast: {} | null; + ast: t.File | null; code: string | null; map: SourceMap | null; sourceType: "string" | "module"; diff --git a/packages/babel-helper-fixtures/src/index.ts b/packages/babel-helper-fixtures/src/index.ts index 8f3189881b0e..b30a2cf73e4d 100644 --- a/packages/babel-helper-fixtures/src/index.ts +++ b/packages/babel-helper-fixtures/src/index.ts @@ -3,56 +3,81 @@ import path from "path"; import fs from "fs"; import { fileURLToPath } from "url"; import { createRequire } from "module"; +import type { InputOptions } from "@babel/core"; +import type { EncodedSourceMap, Mapping } from "@jridgewell/gen-mapping"; const require = createRequire(import.meta.url); const nodeVersion = semver.clean(process.version.slice(1)); -function humanize(val, noext?) { +function humanize(val: string, noext?: boolean) { if (noext) val = path.basename(val, path.extname(val)); return val.replace(/-/g, " "); } -type TestFile = { +interface TestIO { loc: string; code: string; +} + +export interface TestFile extends TestIO { filename: string; -}; +} -type Test = { +export interface Test { + taskDir: string; title: string; disabled: boolean; - options: any; + options: TaskOptions; + optionsDir: string; + doNotSetSourceType: boolean; + externalHelpers: boolean; + ignoreOutput: boolean; + stdout: TestIO; + stderr: TestIO; exec: TestFile; actual: TestFile; - expected: TestFile; - // todo(flow->ts): improve types here - sourceMappings; - sourceMap; + expect: TestFile; + inputSourceMap?: EncodedSourceMap; + sourceMappings?: Mapping[]; + sourceMap: string; sourceMapFile: TestFile; -}; + validateLogs: boolean; +} + +interface TaskOptions extends InputOptions { + BABEL_8_BREAKING?: boolean; + DO_NOT_SET_SOURCE_TYPE?: boolean; + externalHelpers?: boolean; + ignoreOutput?: boolean; + minNodeVersion?: string; + sourceMap?: boolean; + os?: string | string[]; + validateLogs?: boolean; + throws?: boolean | string; +} type Suite = { - options: any; + options: TaskOptions; tests: Array; title: string; filename: string; }; -function tryResolve(module) { +function tryResolve(module: string) { try { return require.resolve(module); } catch (e) { return null; } } -function assertDirectory(loc) { +function assertDirectory(loc: string) { if (!fs.statSync(loc).isDirectory()) { throw new Error(`Expected ${loc} to be a directory.`); } } -function shouldIgnore(name, ignore?: Array) { +function shouldIgnore(name: string, ignore?: Array) { if (ignore && ignore.indexOf(name) >= 0) { return true; } @@ -87,7 +112,12 @@ function findFile(filepath: string, allowJSON?: boolean) { return matches[0]; } -function pushTask(taskName, taskDir, suite, suiteName) { +function pushTask( + taskName: string, + taskDir: string, + suite: Suite, + suiteName: string, +) { const taskDirStats = fs.statSync(taskDir); let actualLoc = findFile(taskDir + "/input"); let execLoc = findFile(taskDir + "/exec"); @@ -126,12 +156,12 @@ function pushTask(taskName, taskDir, suite, suiteName) { execLocAlias = suiteName + "/" + taskName; } - const taskOpts = JSON.parse(JSON.stringify(suite.options)); + const taskOpts: TaskOptions = JSON.parse(JSON.stringify(suite.options)); const taskOptsLoc = tryResolve(taskDir + "/options"); if (taskOptsLoc) Object.assign(taskOpts, require(taskOptsLoc)); - const test = { + const test: Test = { taskDir, optionsDir: taskOptsLoc ? path.dirname(taskOptsLoc) : null, title: humanize(taskName, true), @@ -264,7 +294,7 @@ function pushTask(taskName, taskDir, suite, suiteName) { (test.stdout.code ? stdoutLoc : stderrLoc), ); } - if (test.options.ignoreOutput) { + if (test.ignoreOutput) { if (test.expect.code) { throw new Error( "Test cannot ignore its output and also validate it: " + expectLoc, @@ -284,7 +314,11 @@ function pushTask(taskName, taskDir, suite, suiteName) { delete test.options.externalHelpers; } -function wrapPackagesArray(type, names, optionsDir) { +function wrapPackagesArray( + type: "plugin" | "preset", + names: (string | [string, object?, string?])[], + optionsDir: string, +) { return names.map(function (val) { if (typeof val === "string") val = [val]; @@ -362,10 +396,10 @@ export function resolveOptionPluginOrPreset( return options; } -export default function get(entryLoc): Array { +export default function get(entryLoc: string): Array { const suites = []; - let rootOpts = {}; + let rootOpts: TaskOptions = {}; const rootOptsLoc = tryResolve(entryLoc + "/options"); if (rootOptsLoc) rootOpts = require(rootOptsLoc); @@ -374,7 +408,7 @@ export default function get(entryLoc): Array { const suite = { options: { ...rootOpts }, - tests: [], + tests: [] as Test[], title: humanize(suiteName), filename: entryLoc + "/" + suiteName, }; @@ -398,8 +432,8 @@ export default function get(entryLoc): Array { return suites; } -export function multiple(entryLoc, ignore?: Array) { - const categories = {}; +export function multiple(entryLoc: string, ignore?: Array) { + const categories: Record = {}; for (const name of fs.readdirSync(entryLoc)) { if (shouldIgnore(name, ignore)) continue; @@ -413,7 +447,7 @@ export function multiple(entryLoc, ignore?: Array) { return categories; } -export function readFile(filename) { +export function readFile(filename: string) { if (fs.existsSync(filename)) { let file = fs.readFileSync(filename, "utf8").trimRight(); file = file.replace(/\r\n/g, "\n"); diff --git a/packages/babel-helper-plugin-test-runner/src/index.ts b/packages/babel-helper-plugin-test-runner/src/index.ts index 44e5a7d56f32..f5a43239e7a2 100644 --- a/packages/babel-helper-plugin-test-runner/src/index.ts +++ b/packages/babel-helper-plugin-test-runner/src/index.ts @@ -2,7 +2,7 @@ import testRunner from "@babel/helper-transform-fixture-test-runner"; import path from "path"; import { URL } from "url"; -export default function (loc) { +export default function (loc: string) { if (!process.env.BABEL_8_BREAKING) { if (!loc.startsWith("file://")) { const name = path.basename(path.dirname(loc)); diff --git a/packages/babel-helper-transform-fixture-test-runner/src/helpers.ts b/packages/babel-helper-transform-fixture-test-runner/src/helpers.ts index 4df3ae81e847..7c15a524432e 100644 --- a/packages/babel-helper-transform-fixture-test-runner/src/helpers.ts +++ b/packages/babel-helper-transform-fixture-test-runner/src/helpers.ts @@ -1,7 +1,7 @@ -export function assertNoOwnProperties(obj) { +export function assertNoOwnProperties(obj: {}) { expect(Object.getOwnPropertyNames(obj)).toHaveLength(0); } -export function multiline(arr) { +export function multiline(arr: string[]) { return arr.join("\n"); } diff --git a/packages/babel-helper-transform-fixture-test-runner/src/index.ts b/packages/babel-helper-transform-fixture-test-runner/src/index.ts index 45c1f1b135cc..49ae396c4d69 100644 --- a/packages/babel-helper-transform-fixture-test-runner/src/index.ts +++ b/packages/babel-helper-transform-fixture-test-runner/src/index.ts @@ -1,9 +1,15 @@ /* eslint-env jest */ import * as babel from "@babel/core"; -import { buildExternalHelpers } from "@babel/core"; +import { + buildExternalHelpers, + type InputOptions, + type FileResult, +} from "@babel/core"; import { default as getFixtures, resolveOptionPluginOrPreset, + type Test, + type TestFile, } from "@babel/helper-fixtures"; import { codeFrameColumns } from "@babel/code-frame"; import { TraceMap, originalPositionFor } from "@jridgewell/trace-mapping"; @@ -20,6 +26,11 @@ const require = createRequire(import.meta.url); import checkDuplicateNodes from "@babel/helper-check-duplicate-nodes"; +type Module = { + id: string; + exports: Record; +}; + if (!process.env.BABEL_8_BREAKING) { // Introduced in Node.js 8 if (!assert.rejects) { @@ -51,14 +62,14 @@ const sharedTestContext = createContext(); // babel.config.js file, so we disable config loading by // default. Tests can still set `configFile: true | string` // to re-enable config loading. -function transformWithoutConfigFile(code, opts) { +function transformWithoutConfigFile(code: string, opts: InputOptions) { return babel.transformSync(code, { configFile: false, babelrc: false, ...opts, }); } -function transformAsyncWithoutConfigFile(code, opts) { +function transformAsyncWithoutConfigFile(code: string, opts: InputOptions) { return babel.transformAsync(code, { configFile: false, babelrc: false, @@ -100,7 +111,7 @@ function runCacheableScriptInTestContext( srcFn: () => string, context: vm.Context, moduleCache: any, -) { +): Module { let cached = cachedScripts.get(filename); if (!cached) { const code = `(function (exports, require, module, __filename, __dirname) {\n${srcFn()}\n});`; @@ -137,7 +148,8 @@ function runCacheableScriptInTestContext( id: filename, exports: {}, }; - const req = id => runModuleInTestContext(id, filename, context, moduleCache); + const req = (id: string) => + runModuleInTestContext(id, filename, context, moduleCache); const dirname = path.dirname(filename); script @@ -195,9 +207,10 @@ export function runCodeInTestContext( const filename = opts.filename; const dirname = path.dirname(filename); const moduleCache = contextModuleCache.get(context); - const req = id => runModuleInTestContext(id, filename, context, moduleCache); + const req = (id: string) => + runModuleInTestContext(id, filename, context, moduleCache); - const module = { + const module: Module = { id: filename, exports: {}, }; @@ -220,7 +233,7 @@ export function runCodeInTestContext( } } -async function maybeMockConsole(validateLogs, run) { +async function maybeMockConsole(validateLogs: boolean, run: () => R) { const actualLogs = { stdout: "", stderr: "" }; if (!validateLogs) return { result: await run(), actualLogs }; @@ -240,7 +253,7 @@ async function maybeMockConsole(validateLogs, run) { } } -async function run(task) { +async function run(task: Test) { const { actual, expect: expected, @@ -255,7 +268,7 @@ async function run(task) { } = task; // todo(flow->ts) add proper return type (added any, because empty object is inferred) - function getOpts(self): any { + function getOpts(self: TestFile): any { const newOpts = { ast: true, cwd: path.dirname(self.loc), @@ -273,7 +286,7 @@ async function run(task) { } let execCode = exec.code; - let result; + let result: FileResult; let resultExec; if (execCode) { @@ -387,7 +400,11 @@ async function run(task) { } } -function validateFile(actualCode, expectedLoc, expectedCode) { +function validateFile( + actualCode: string, + expectedLoc: string, + expectedCode: string, +) { try { expect(actualCode).toEqualFile({ filename: expectedLoc, @@ -401,11 +418,11 @@ function validateFile(actualCode, expectedLoc, expectedCode) { } } -function escapeRegExp(string) { +function escapeRegExp(string: string) { return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&"); } -function normalizeOutput(code, normalizePathSeparator?) { +function normalizeOutput(code: string, normalizePathSeparator?: boolean) { const projectRoot = path.resolve( path.dirname(fileURLToPath(import.meta.url)), "../../../", @@ -438,7 +455,7 @@ function normalizeOutput(code, normalizePathSeparator?) { } expect.extend({ - toEqualFile(actual, { filename, code }) { + toEqualFile(actual, { filename, code }: Pick) { if (this.isNot) { throw new Error(".toEqualFile does not support negation"); } @@ -465,7 +482,10 @@ declare global { namespace jest { // eslint-disable-next-line @typescript-eslint/no-unused-vars interface Matchers { - toEqualFile({ filename, code }): jest.CustomMatcherResult; + toEqualFile({ + filename, + code, + }: Pick): jest.CustomMatcherResult; } } } @@ -508,12 +528,13 @@ export default function ( if (dynamicOpts) dynamicOpts(task.options, task); - // @ts-expect-error todo(flow->ts) missing property if (task.externalHelpers) { - (task.options.plugins ??= []).push([ - "external-helpers", - { helperVersion: EXTERNAL_HELPERS_VERSION }, - ]); + (task.options.plugins ??= []) + // @ts-ignore manipulating input options + .push([ + "external-helpers", + { helperVersion: EXTERNAL_HELPERS_VERSION }, + ]); } const throwMsg = task.options.throws;