From cfa4b2524b800e3dd26fd77ae8579ce6952f5240 Mon Sep 17 00:00:00 2001 From: Christian Falch Date: Fri, 24 Jul 2026 09:47:23 +0200 Subject: [PATCH] SPM: fix raw Flow type annotations in bare-node scripts The scripts under `scripts/spm/` are executed as plain `node` (via `setup-apple-spm.js` during the SwiftPM Xcode build), with no Babel to strip Flow. They therefore use Flow-in-comment syntax (`/*: T */`) throughout so they parse un-transpiled. A handful of uninitialized `let X: T;` declarations had slipped in with raw annotations. Node parses the whole file on `require`, so each one threw `SyntaxError: Unexpected token ':'` at load time, taking the entire module down. The jest suites didn't catch it because jest runs these files through @react-native/babel-preset, which strips raw and comment-form annotations alike. Convert them to Flow-comment form. Prettier's `flow` parser only keeps a comment type attached to the binding when the declaration is initialized, so each site gets a type-appropriate initializer (the variable is always reassigned in the immediately-following try/branch before any use, so the initializer is inert): autolinking-plugins.js 120, 178 /*: unknown */ = null download-spm-artifacts.js 946, 973 /*: string */ = '' download-spm-artifacts.js 1352 /*: {...} */ = {} generate-spm-autolinking.js 429 /*: Array<...> */ = [] generate-spm-xcodeproj.js 1802 /*: string */ = '' generate-spm-xcodeproj.js 1808 /*: unknown */ = null generate-spm-xcodeproj.js 1863 /*: Array<...> */ = [] scaffold-package-swift.js 200, 1129 /*: Array<...> */ = [] scaffold-package-swift.js 933 (annotation dropped; Flow infers PodspecModel from readPodspec()) `unknown` sites use `= null` rather than `= undefined` to satisfy the `no-undef-init` lint rule. Verified: `node --check` passes on all 14 spm scripts; eslint (--max-warnings 0), prettier, and `flow focus-check` are all clean on the touched files; the spm jest suites remain green. Co-Authored-By: Claude Opus 4.8 --- packages/react-native/scripts/spm/autolinking-plugins.js | 4 ++-- packages/react-native/scripts/spm/download-spm-artifacts.js | 6 +++--- .../react-native/scripts/spm/generate-spm-autolinking.js | 2 +- packages/react-native/scripts/spm/generate-spm-xcodeproj.js | 6 +++--- packages/react-native/scripts/spm/scaffold-package-swift.js | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/react-native/scripts/spm/autolinking-plugins.js b/packages/react-native/scripts/spm/autolinking-plugins.js index e2b18a979dc7..c218a3960264 100644 --- a/packages/react-native/scripts/spm/autolinking-plugins.js +++ b/packages/react-native/scripts/spm/autolinking-plugins.js @@ -117,7 +117,7 @@ function discoverPlugins( ); } const pluginPath = path.resolve(dep.root, rel); - let fn: unknown; + let fn /*: unknown */ = null; try { // $FlowFixMe[unsupported-syntax] dynamic require by computed path fn = require(pluginPath); @@ -175,7 +175,7 @@ function invokePlugins( const seenFrameworkNames /*: Set */ = new Set(); for (const {depName, pluginPath, plugin} of plugins) { - let result: unknown; + let result /*: unknown */ = null; try { result = plugin(context); } catch (e) { diff --git a/packages/react-native/scripts/spm/download-spm-artifacts.js b/packages/react-native/scripts/spm/download-spm-artifacts.js index 416db6080b8e..4faa4331154c 100644 --- a/packages/react-native/scripts/spm/download-spm-artifacts.js +++ b/packages/react-native/scripts/spm/download-spm-artifacts.js @@ -943,7 +943,7 @@ async function processArtifact( return localPath; }; - let tarPath: string; + let tarPath /*: string */ = ''; let fromShared = false; if (isLocalTarball) { tarPath = url; @@ -970,7 +970,7 @@ async function processArtifact( onProgress(xcframeworkName, 0, 0, 0, false, 0); } const tmpExtractDir = path.join(outputDir, '.extract-tmp', label); - let xcfwPath: string; + let xcfwPath /*: string */ = ''; try { xcfwPath = extractXCFramework(tarPath, tmpExtractDir); } catch (e) { @@ -1349,7 +1349,7 @@ function validateArtifactsCache( if (!fs.existsSync(artifactsJsonPath)) { return `artifacts.json missing in ${artifactsDir}`; } - let json: {[string]: {xcframeworkPath: string, url: string}}; + let json /*: {[string]: {xcframeworkPath: string, url: string}} */ = {}; try { // $FlowFixMe[unclear-type] JSON.parse returns any const parsed /*: any */ = JSON.parse( diff --git a/packages/react-native/scripts/spm/generate-spm-autolinking.js b/packages/react-native/scripts/spm/generate-spm-autolinking.js index 78cada1ad91c..058d6c3e4da2 100644 --- a/packages/react-native/scripts/spm/generate-spm-autolinking.js +++ b/packages/react-native/scripts/spm/generate-spm-autolinking.js @@ -426,7 +426,7 @@ function hasMixedLanguageSources(absSource /*: string */) /*: boolean */ { let hasClang = false; const walk = (dir /*: string */, depth /*: number */) => { if (depth > 6 || (hasSwift && hasClang)) return; - let entries: Array<{name: string, isDirectory(): boolean}>; + let entries /*: Array<{name: string, isDirectory(): boolean}> */ = []; try { // $FlowFixMe[incompatible-type] Dirent typing entries = fs.readdirSync(dir, {withFileTypes: true}); diff --git a/packages/react-native/scripts/spm/generate-spm-xcodeproj.js b/packages/react-native/scripts/spm/generate-spm-xcodeproj.js index 0bb3467995c5..e75af429ce29 100644 --- a/packages/react-native/scripts/spm/generate-spm-xcodeproj.js +++ b/packages/react-native/scripts/spm/generate-spm-xcodeproj.js @@ -1799,13 +1799,13 @@ function readGeneratedSourcesManifest( appRoot /*: string */, ) /*: Array */ { const manifestPath = path.join(appRoot, SPM_GENERATED_SOURCES_MANIFEST); - let raw: string; + let raw /*: string */ = ''; try { raw = fs.readFileSync(manifestPath, 'utf8'); } catch { return []; } - let entries: unknown; + let entries /*: unknown */ = null; try { entries = JSON.parse(raw); } catch { @@ -1860,7 +1860,7 @@ function readMarker( // build-time sync (sync-spm-autolinking.js, via readArtifactsVersionOverride // below) to call without pulling in any pbxproj-editing machinery at runtime. function findInjectedXcodeproj(appRoot /*: string */) /*: string | null */ { - let entries: Array<{name: string, isDirectory(): boolean}>; + let entries /*: Array<{name: string, isDirectory(): boolean}> */ = []; try { // $FlowFixMe[incompatible-type] Dirent typing entries = fs.readdirSync(appRoot, {withFileTypes: true}); diff --git a/packages/react-native/scripts/spm/scaffold-package-swift.js b/packages/react-native/scripts/spm/scaffold-package-swift.js index 073ea0a23962..6d52f60760b8 100644 --- a/packages/react-native/scripts/spm/scaffold-package-swift.js +++ b/packages/react-native/scripts/spm/scaffold-package-swift.js @@ -197,7 +197,7 @@ function collectSubdirs( ]); const out /*: Array */ = []; const walk = (absDir /*: string */, relDir /*: string */) => { - let entries: Array<{name: string, isDirectory(): boolean}>; + let entries /*: Array<{name: string, isDirectory(): boolean}> */ = []; try { // $FlowFixMe[incompatible-type] Dirent typing entries = fs.readdirSync(absDir, {withFileTypes: true}); @@ -930,7 +930,7 @@ function scaffoldPackageSwiftForDep( }; } - let model: PodspecModel; + let model; try { model = readPodspec(podspecPath); } catch (e) { @@ -1126,7 +1126,7 @@ function scaffoldAll( directDeps.push({name, root, platforms: {ios: iosPlatform}}); } - let allDeps: Array; + let allDeps /*: Array */ = []; try { allDeps = expandSpmDependencies(directDeps, { readConfig: defaultReadConfig,