From 62d5cffcfbb79a83d84ef89b30a7feb94a749e78 Mon Sep 17 00:00:00 2001 From: Rob Hogan Date: Tue, 29 Sep 2026 16:12:49 +0100 Subject: [PATCH] TypeScript: Generate CommonJS module types with flow-api-translator 0.333 Summary: `flow-api-translator` 0.332.0 translates `declare module.exports` to TypeScript's `export =` (https://github.com/facebook/flow/pull/9491). Until now `scripts/generateTypeScriptDefinitions.js` rejected any module whose Flow def used it, which is why all of `metro-runtime` bar `modules/types.js` was excluded, and why most of our hand-maintained `src/**/*.d.ts` overrides exist. With `flow-api-translator` on 0.333.0, this drops the `module.exports` rejection and the `metro-runtime` exclusion, and removes as many overrides as the translator can now replace: - `metro-runtime` is generated like any other package - `asyncRequire`, `HMRClient`, `null-module` and `polyfills/require` gain types and API snapshots. - Eight overrides go: `metro-minify-terser` and `metro-symbolicate` (generated output is equivalent), the five CommonJS worker and extractor modules in `metro-file-map`, and `metro-file-map/src/lib/dependencyExtractor.d.ts`, which had no `.js` beside it and so was never used. - Two stay: `metro-transform-plugins/src/index.d.ts`, because it combines `export type` with `module.exports`, which TypeScript can't express as `export =` without namespace merging; and `crawlers/watchman/planQuery.d.ts`, because the generated output imports types from `fb-watchman`, which has none. Getting there needed a few changes to the generator and to Flow sources, none of which affect runtime behaviour: - Flow comment types (`/*: T */`, `/*:: ... */`) are expanded before translation. The `metro-file-map` workers are loaded in Node without Babel and are typed that way, so the translator saw no annotations at all and emitted `any`. - A `.js.flow` file is used as the Flow source for the adjacent `.js`. That's Flow's own convention, and it types the vendored `eventemitter3` (no `@flow` directive) without an override. - `lintText` returns no result for a path `.eslintignore` covers (`**/vendor/**`), so the generator tolerates that and leaves the output unlinted. - The two `metro-file-map` plugin workers now declare their class and export it by name, rather than exporting an anonymous class expression, which `flowToFlowDef` can't type. - `asyncRequire` exports through an `as AsyncRequire` cast - `flowToFlowDef` drops statics assigned to a function, so `prefetch` and `unstable_importMaybeSync` were otherwise lost. The cast is type-only, and `metro-runtime` already uses `as` casts elsewhere. - `HMRClient._heartbeatTimer` is typed `?ReturnType` in place of Flow's `IntervalID` global, which the translator passes through and TypeScript has no name for. - Generated `.d.ts` no longer run `@typescript-eslint`'s `no-explicit-any` or `no-unused-vars`. Flow checks the sources these are translated from, and an `any` in the output reflects one there; the second rule reports a declaration a CommonJS module exports via `typeof` as unused, which is most of what `metro-runtime` and the `metro-file-map` workers now emit. `no-unused-vars` stays on in the generator's own `--fix` pass, which uses it to drop declarations left unused by translation. `no-empty-object-type` is off too, for now: Flow's inexact `{...}` translates to `{}`, which the rule flags in `polyfills/require.d.ts` - the follow-up diff fixes those annotations at source and turns the rule back on. This replaces the per-file exemption for `metro-file-map/types/flow-types.d.ts`. Changelog: ``` - **[Types]**: Add TypeScript definitions for `metro-runtime` modules, and generate (rather than hand-maintain) types for `metro-file-map` workers ``` Test plan: ``` yarn flow check yarn typecheck-ts yarn build-api-snapshots yarn verify-api-snapshots yarn jest ``` Flow and `tsc` are clean. API snapshots for ES module entry points are unchanged, so removing the overrides changes no public API. `metro-runtime` gains four snapshots, and `metro-minify-terser`'s now reflects its generated definition. Jest: all 150 suites pass, including the `metro` integration tests, which bundle `metro-runtime`. --- .eslintrc.js | 9 --- .../src/lib/dependencyExtractor.d.ts | 14 ---- .../dependencies/dependencyExtractor.d.ts | 14 ---- .../src/plugins/dependencies/worker.d.ts | 24 ------- .../src/plugins/dependencies/worker.js | 6 +- .../src/plugins/haste/worker.d.ts | 24 ------- .../src/plugins/haste/worker.js | 6 +- packages/metro-file-map/src/worker.d.ts | 36 ---------- .../src/workerExclusionList.d.ts | 12 ---- packages/metro-minify-terser/API.md | 6 +- packages/metro-minify-terser/src/index.d.ts | 14 ---- .../metro-runtime/API-modules-HMRClient.md | 20 ++++++ .../metro-runtime/API-modules-asyncRequire.md | 22 +++++++ .../metro-runtime/API-modules-null-module.md | 12 ++++ .../metro-runtime/API-polyfills-require.md | 41 ++++++++++++ .../metro-runtime/src/modules/asyncRequire.js | 21 +++++- packages/metro-symbolicate/src/index.d.ts | 11 ---- scripts/eslint/typescript.js | 14 ++++ scripts/generateTypeScriptDefinitions.js | 66 ++++++++++++------- 19 files changed, 184 insertions(+), 188 deletions(-) delete mode 100644 packages/metro-file-map/src/lib/dependencyExtractor.d.ts delete mode 100644 packages/metro-file-map/src/plugins/dependencies/dependencyExtractor.d.ts delete mode 100644 packages/metro-file-map/src/plugins/dependencies/worker.d.ts delete mode 100644 packages/metro-file-map/src/plugins/haste/worker.d.ts delete mode 100644 packages/metro-file-map/src/worker.d.ts delete mode 100644 packages/metro-file-map/src/workerExclusionList.d.ts delete mode 100644 packages/metro-minify-terser/src/index.d.ts create mode 100644 packages/metro-runtime/API-modules-HMRClient.md create mode 100644 packages/metro-runtime/API-modules-asyncRequire.md create mode 100644 packages/metro-runtime/API-modules-null-module.md create mode 100644 packages/metro-runtime/API-polyfills-require.md delete mode 100644 packages/metro-symbolicate/src/index.d.ts diff --git a/.eslintrc.js b/.eslintrc.js index 54373f38e6..4c3ed2b85b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -60,14 +60,5 @@ module.exports = { 'import/no-commonjs': 'off', }, }, - { - files: [ - // flow-api-translator doesn't support translating `empty` - 'packages/metro-file-map/types/flow-types.d.ts', - ], - rules: { - '@typescript-eslint/no-explicit-any': 'off', - }, - }, ], }; diff --git a/packages/metro-file-map/src/lib/dependencyExtractor.d.ts b/packages/metro-file-map/src/lib/dependencyExtractor.d.ts deleted file mode 100644 index 934a76244e..0000000000 --- a/packages/metro-file-map/src/lib/dependencyExtractor.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -declare const dependencyExtractor: { - extract: (code: string) => Set; -}; - -export = dependencyExtractor; diff --git a/packages/metro-file-map/src/plugins/dependencies/dependencyExtractor.d.ts b/packages/metro-file-map/src/plugins/dependencies/dependencyExtractor.d.ts deleted file mode 100644 index b8cf6d95c9..0000000000 --- a/packages/metro-file-map/src/plugins/dependencies/dependencyExtractor.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @generated by js1 build metro-ts-defs / yarn run build-ts-defs - */ - -declare const dependencyExtractor: { - extract(code: string): Set; -}; - -export = dependencyExtractor; diff --git a/packages/metro-file-map/src/plugins/dependencies/worker.d.ts b/packages/metro-file-map/src/plugins/dependencies/worker.d.ts deleted file mode 100644 index c7b79538f0..0000000000 --- a/packages/metro-file-map/src/plugins/dependencies/worker.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @oncall react_native - */ - -import type { - MetadataWorker, - V8Serializable, - WorkerMessage, -} from '../../flow-types'; - -declare class DependencyExtractorWorker implements MetadataWorker { - constructor(opts: Readonly<{dependencyExtractor: null | undefined | string}>); - processFile( - data: WorkerMessage, - utils: Readonly<{getContent: () => Buffer}>, - ): V8Serializable; -} -export = DependencyExtractorWorker; diff --git a/packages/metro-file-map/src/plugins/dependencies/worker.js b/packages/metro-file-map/src/plugins/dependencies/worker.js index 2828e2e02c..ce5eb3e7f4 100644 --- a/packages/metro-file-map/src/plugins/dependencies/worker.js +++ b/packages/metro-file-map/src/plugins/dependencies/worker.js @@ -19,7 +19,7 @@ const defaultDependencyExtractor = require('./dependencyExtractor'); import type {MetadataWorker, WorkerMessage, V8Serializable, DependencyExtractor} from '../../flow-types'; */ -module.exports = class DependencyExtractorWorker /*:: implements MetadataWorker */ { +class DependencyExtractorWorker /*:: implements MetadataWorker */ { // prettier-ignore /*:: readonly */ #dependencyExtractor /*: ?DependencyExtractor['extract'] */; @@ -56,4 +56,6 @@ module.exports = class DependencyExtractorWorker /*:: implements MetadataWorker // Return as array (PerFileData type) return Array.from(dependencies); } -}; +} + +module.exports = DependencyExtractorWorker; diff --git a/packages/metro-file-map/src/plugins/haste/worker.d.ts b/packages/metro-file-map/src/plugins/haste/worker.d.ts deleted file mode 100644 index eec44e52da..0000000000 --- a/packages/metro-file-map/src/plugins/haste/worker.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -import type { - MetadataWorker, - V8Serializable, - WorkerMessage, -} from '../../flow-types'; - -declare class Worker implements MetadataWorker { - constructor(opts: Readonly<{hasteImplModulePath: null | undefined | string}>); - processFile( - data: WorkerMessage, - utils: Readonly<{getContent: () => Buffer}>, - ): V8Serializable; -} - -export = Worker; diff --git a/packages/metro-file-map/src/plugins/haste/worker.js b/packages/metro-file-map/src/plugins/haste/worker.js index 63254a9ae2..6eb0a2f3e5 100644 --- a/packages/metro-file-map/src/plugins/haste/worker.js +++ b/packages/metro-file-map/src/plugins/haste/worker.js @@ -21,7 +21,7 @@ import type {MetadataWorker, WorkerMessage, V8Serializable} from '../../flow-typ const PACKAGE_JSON = path.sep + 'package.json'; -module.exports = class Worker /*:: implements MetadataWorker */ { +class Worker /*:: implements MetadataWorker */ { /*:: readonly */ #hasteImpl /*: ?Readonly<{getHasteName: string => ?string}> */ = null; @@ -63,4 +63,6 @@ module.exports = class Worker /*:: implements MetadataWorker */ { } return hasteName; } -}; +} + +module.exports = Worker; diff --git a/packages/metro-file-map/src/worker.d.ts b/packages/metro-file-map/src/worker.d.ts deleted file mode 100644 index 1c1e3d94c2..0000000000 --- a/packages/metro-file-map/src/worker.d.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -import type { - WorkerMessage, - WorkerMetadata, - WorkerSetupArgs, -} from './flow-types'; - -declare const worker: { - /** - * Called automatically by jest-worker before the first call to `worker` when - * this module is used as worker thread or child process. - */ - setup: (args: WorkerSetupArgs) => void; - /** - * Called by jest-worker with each workload - */ - processFile: (data: WorkerMessage) => WorkerMetadata; - /** - * Exposed for use outside a jest-worker context, ie when processing in-band. - */ - Worker: { - new (setupArgs: WorkerSetupArgs): { - processFile(data: WorkerMessage): WorkerMetadata; - }; - }; -}; - -export = worker; diff --git a/packages/metro-file-map/src/workerExclusionList.d.ts b/packages/metro-file-map/src/workerExclusionList.d.ts deleted file mode 100644 index ac3fba8b76..0000000000 --- a/packages/metro-file-map/src/workerExclusionList.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -declare const exclusionList: ReadonlySet; - -export = exclusionList; diff --git a/packages/metro-minify-terser/API.md b/packages/metro-minify-terser/API.md index 3cde1aeebe..45e1507f12 100644 --- a/packages/metro-minify-terser/API.md +++ b/packages/metro-minify-terser/API.md @@ -4,7 +4,9 @@ ```ts -const minifierFn: typeof minifier; -export default minifierFn; +const $$MODULE_EXPORTS$$: typeof minifier; + +type $$MODULE_EXPORTS$$ = typeof $$MODULE_EXPORTS$$; +export default $$MODULE_EXPORTS$$; ``` diff --git a/packages/metro-minify-terser/src/index.d.ts b/packages/metro-minify-terser/src/index.d.ts deleted file mode 100644 index 39ef5fc46f..0000000000 --- a/packages/metro-minify-terser/src/index.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @oncall react_native - */ - -import minifier from './minifier'; - -declare const minifierFn: typeof minifier; -export = minifierFn; diff --git a/packages/metro-runtime/API-modules-HMRClient.md b/packages/metro-runtime/API-modules-HMRClient.md new file mode 100644 index 0000000000..1849e12ca1 --- /dev/null +++ b/packages/metro-runtime/API-modules-HMRClient.md @@ -0,0 +1,20 @@ +## API Report File for "metro-runtime" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +import EventEmitter = require('./vendor/eventemitter3'); + +class HMRClient extends EventEmitter { + constructor(url: string); + close(): void; + disable(): void; + enable(): void; + hasPendingUpdates(): boolean; + isEnabled(): boolean; + send(message: string): void; +} +export default HMRClient; + +``` diff --git a/packages/metro-runtime/API-modules-asyncRequire.md b/packages/metro-runtime/API-modules-asyncRequire.md new file mode 100644 index 0000000000..49ae822ea4 --- /dev/null +++ b/packages/metro-runtime/API-modules-asyncRequire.md @@ -0,0 +1,22 @@ +## API Report File for "metro-runtime" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +const $$MODULE_EXPORTS$$: AsyncRequire; + +type $$MODULE_EXPORTS$$ = typeof $$MODULE_EXPORTS$$; +export default $$MODULE_EXPORTS$$; + +export type AsyncRequire = { + (moduleID: number, paths: DependencyMapPaths, moduleName?: string): Promise; + unstable_importMaybeSync(moduleID: number, paths: DependencyMapPaths): Promise | T; + prefetch(moduleID: number, paths: DependencyMapPaths, moduleName?: string): void; +}; + +export type DependencyMapPaths = null | undefined | Readonly<{[moduleID: number | string]: ReadonlyJsonData}>; + +export type ReadonlyJsonData = null | boolean | number | string | ReadonlyArray | Readonly<{[$$Key$$: string]: ReadonlyJsonData}>; + +``` diff --git a/packages/metro-runtime/API-modules-null-module.md b/packages/metro-runtime/API-modules-null-module.md new file mode 100644 index 0000000000..a7ade4eeda --- /dev/null +++ b/packages/metro-runtime/API-modules-null-module.md @@ -0,0 +1,12 @@ +## API Report File for "metro-runtime" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +const $$MODULE_EXPORTS$$: null; + +type $$MODULE_EXPORTS$$ = typeof $$MODULE_EXPORTS$$; +export default $$MODULE_EXPORTS$$; + +``` diff --git a/packages/metro-runtime/API-polyfills-require.md b/packages/metro-runtime/API-polyfills-require.md new file mode 100644 index 0000000000..d0a5fdf23b --- /dev/null +++ b/packages/metro-runtime/API-polyfills-require.md @@ -0,0 +1,41 @@ +## API Report File for "metro-runtime" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +export type ArrayIndexable = Readonly<{[indexer: number]: T}>; + +export type DefineFn = (factory: FactoryFn, moduleId: number, dependencyMap?: DependencyMap, verboseName?: string, inverseDependencies?: InverseDependencyMap) => void; + +export type DependencyMap = Readonly< + ArrayIndexable & { + paths?: {[id in ModuleID]: string} | undefined; + } +>; + +export type Exports = any; + +export type FactoryFn = ( + global: object, + require: RequireFn, + metroImportDefault: RequireFn, + metroImportAll: RequireFn, + moduleObject: {exports: {}}, + exports: {}, + dependencyMap: null | undefined | DependencyMap, +) => void; + +export type InverseDependencyMap = {[key in ModuleID]: Array}; + +export function metroImportAll(moduleId: ModuleID | VerboseModuleNameForDev | number): any | Exports | {[$$Key$$: string]: any}; + +export function metroImportDefault(moduleId: ModuleID | VerboseModuleNameForDev): any | Exports; + +export type ModuleID = number; + +export type RequireFn = (id: ModuleID | VerboseModuleNameForDev) => Exports; + +export type VerboseModuleNameForDev = string; + +``` diff --git a/packages/metro-runtime/src/modules/asyncRequire.js b/packages/metro-runtime/src/modules/asyncRequire.js index c2fab9e1f9..400416fdf7 100644 --- a/packages/metro-runtime/src/modules/asyncRequire.js +++ b/packages/metro-runtime/src/modules/asyncRequire.js @@ -32,6 +32,24 @@ type DependencyMapPaths = ?Readonly<{ [moduleID: number | string]: ReadonlyJsonData, }>; +type AsyncRequire = { + ( + moduleID: number, + paths: DependencyMapPaths, + moduleName?: string, + ): Promise, + unstable_importMaybeSync( + moduleID: number, + paths: DependencyMapPaths, + ): Promise | T, + prefetch( + moduleID: number, + paths: DependencyMapPaths, + moduleName?: string, + ): void, + ... +}; + declare var __METRO_GLOBAL_PREFIX__: string; function maybeLoadBundle( @@ -97,4 +115,5 @@ asyncRequire.prefetch = function ( ); }; -module.exports = asyncRequire; +// The explicit type carries the statics through to generated type definitions. +module.exports = asyncRequire as AsyncRequire; diff --git a/packages/metro-symbolicate/src/index.d.ts b/packages/metro-symbolicate/src/index.d.ts deleted file mode 100644 index 84510675f4..0000000000 --- a/packages/metro-symbolicate/src/index.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @oncall react_native - */ - -// No exports diff --git a/scripts/eslint/typescript.js b/scripts/eslint/typescript.js index 42c393b58a..c79351dbba 100644 --- a/scripts/eslint/typescript.js +++ b/scripts/eslint/typescript.js @@ -27,4 +27,18 @@ module.exports = { ], plugins: ['@typescript-eslint'], parser: '@typescript-eslint/parser', + rules: { + // These files are generated from Flow sources, which Flow itself checks - + // an `any` here reflects one in the Flow source, or a construct the + // translator can't express. + '@typescript-eslint/no-explicit-any': 'off', + // Noise in generated definitions: a declaration a CommonJS module exports + // via `typeof` is reported as unused. The generator runs this rule itself, + // with `--fix`, to drop declarations that really are unused. + '@typescript-eslint/no-unused-vars': 'off', + // Flow's inexact `{...}` translates to `{}`, which this rule flags. The + // Flow sources that produce one are worth fixing rather than silencing, so + // this is off only until they are. + '@typescript-eslint/no-empty-object-type': 'off', + }, }; diff --git a/scripts/generateTypeScriptDefinitions.js b/scripts/generateTypeScriptDefinitions.js index bc91beef5a..d13a2721b9 100644 --- a/scripts/generateTypeScriptDefinitions.js +++ b/scripts/generateTypeScriptDefinitions.js @@ -56,7 +56,6 @@ const IGNORED_PATTERNS = [ 'packages/metro/src/cli.js', 'packages/**/third-party', 'packages/metro/src/integration_tests', - 'packages/metro-runtime/**/!(types*).js', ]; function isSourceTSDeclaration(filePath: string): boolean { @@ -82,6 +81,13 @@ export async function generateTsDefsForJsGlobs( fix: true, cwd: WORKSPACE_ROOT, overrideConfig: { + rules: { + // Off when linting the repo, because a declaration exported via + // `typeof` is reported as unused - but the generator relies on it to + // drop declarations left unused by translation, and acts only on + // "is defined but never used". + '@typescript-eslint/no-unused-vars': 'error', + }, parserOptions: { // typescript-eslint writes its "version of TypeScript which is not // officially supported" warning straight to `console.log`, bypassing @@ -138,6 +144,10 @@ export async function generateTsDefsForJsGlobs( // corresponding `.js` file, which is enforced to be a transparent // entry file that only registers Babel and re-exports the module. toProcess.set(filePath.replace(/\.flow\.js$/, '.js'), filePath); + } else if (filePath.endsWith('.js.flow')) { + // A .js.flow declaration file is the Flow source of truth for the + // adjacent .js file, which may be untyped (e.g. vendored code). + toProcess.set(filePath.replace(/\.flow$/, ''), filePath); } else if (filePath.endsWith('.js') && !toProcess.has(filePath)) { toProcess.set(filePath, filePath); } else if (isSourceTSDeclaration(filePath)) { @@ -157,23 +167,28 @@ export async function generateTsDefsForJsGlobs( absoluteTsFile: string, sourceFile: string, ) { - // Lint and fix the generated output + // Lint and fix the generated output. `lintText` returns no result at all + // for a path .eslintignore covers - `**/vendor/**`, for one - in which case + // the output stands unlinted. let [lintResult] = await linter.lintText(sourceContent, { filePath: absoluteTsFile, }); - let lintedOutput = lintResult.output ?? sourceContent; + let lintedOutput = lintResult?.output ?? sourceContent; const withoutUnusedGeneratedDeclarations = - removeUnusedGeneratedDeclarations(lintedOutput, lintResult.messages); + removeUnusedGeneratedDeclarations( + lintedOutput, + lintResult?.messages ?? [], + ); if (withoutUnusedGeneratedDeclarations !== lintedOutput) { [lintResult] = await linter.lintText(withoutUnusedGeneratedDeclarations, { filePath: absoluteTsFile, }); - lintedOutput = lintResult.output ?? withoutUnusedGeneratedDeclarations; + lintedOutput = lintResult?.output ?? withoutUnusedGeneratedDeclarations; } - if (logger && lintResult.messages.length > 0) { + if (logger && lintResult != null && lintResult.messages.length > 0) { logger.warn(sourceFile, lintResult.messages); } @@ -249,28 +264,19 @@ export async function generateTsDefsForJsGlobs( } try { const flowDef = await translateFlowToFlowDef( - source, + expandFlowCommentTypes(source), {}, {mungeUnderscores}, ); - if (flowDef.includes('declare module.exports')) { - errors.push({ - sourceFile, - error: new Error( - 'module.exports is not supported by TypeScript auto-generation', - ), - }); - } else { - const tsDef = await translateFlowDefToTSDef(flowDef); + const tsDef = await translateFlowDefToTSDef(flowDef); - const beforeLint = tsDef - // Fix up gap left in license header by removal of atflow - .replace('\n *\n *\n', '\n *\n') - // TypeScript has no analogue for __proto__: null - .replace(/__proto__: null[,;]?/g, ''); + const beforeLint = tsDef + // Fix up gap left in license header by removal of atflow + .replace('\n *\n *\n', '\n *\n') + // TypeScript has no analogue for __proto__: null + .replace(/__proto__: null[,;]?/g, ''); - await writeOutputFile(beforeLint, absoluteTsFile, sourceFile); - } + await writeOutputFile(beforeLint, absoluteTsFile, sourceFile); } catch (error) { errors.push({sourceFile, error}); } @@ -302,6 +308,20 @@ export async function generateTsDefsForJsGlobs( } } +// Files that run in Node without a Babel transform (e.g. worker entry points) +// carry their types in Flow comment syntax, which the translator doesn't see. +// Rewrite them as regular Flow syntax, per +// https://flow.org/en/docs/types/comments/: +// `/*: T */` -> `: T` +// `/*:: code */` and `/*flow-include code */` -> `code` +function expandFlowCommentTypes(source: string): string { + return source.replace( + /\/\*(::|flow-include|:)([\s\S]*?)\*\//g, + (_match, marker: string, content: string) => + marker === ':' ? ':' + content : content, + ); +} + function removeUnusedGeneratedDeclarations( sourceContent: string, messages: ReadonlyArray,