From 39094de1fc3cd1ae2bd3080a02024d0b3b33eade Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Thu, 9 Jan 2025 16:01:48 +0100 Subject: [PATCH] chore: convert internal scripts to `.mts` (#2367) --- .github/workflows/build.yml | 4 +- .../reacttestapp/manifest/Manifest.kt | 2 +- eslint.config.js | 18 ++-- ios/ReactTestApp/Manifest.swift | 2 +- package.json | 24 ++--- scripts/build/{affected.ts => affected.mts} | 0 ...st-docs.mjs => generate-manifest-docs.mts} | 18 ++-- ...ate-manifest.mjs => generate-manifest.mts} | 61 +++++++------ ...enerate-schema.mjs => generate-schema.mts} | 35 +++++--- scripts/internal/{pack.mjs => pack.mts} | 5 +- ...-viewfinder.mjs => prepare-viewfinder.mts} | 8 +- .../{release-notes.mjs => release-notes.mts} | 26 +++--- ...eact-version.mjs => set-react-version.mts} | 90 +++++++------------ scripts/internal/{test.mjs => test.mts} | 15 +--- .../{react-native.mjs => react-native.mts} | 8 +- .../{test-apple.mjs => test-apple.mts} | 15 ++-- .../testing/{test-e2e.mjs => test-e2e.mts} | 23 ++--- .../{test-matrix.mjs => test-matrix.mts} | 66 +++++--------- scripts/types.ts | 31 +------ test/configure/updatePackageManifest.test.ts | 4 +- tsconfig.json | 1 + windows/Shared/Manifest.h | 2 +- 22 files changed, 188 insertions(+), 270 deletions(-) rename scripts/build/{affected.ts => affected.mts} (100%) rename scripts/internal/{generate-manifest-docs.mjs => generate-manifest-docs.mts} (86%) rename scripts/internal/{generate-manifest.mjs => generate-manifest.mts} (85%) rename scripts/internal/{generate-schema.mjs => generate-schema.mts} (70%) rename scripts/internal/{pack.mjs => pack.mts} (91%) rename scripts/internal/{prepare-viewfinder.mjs => prepare-viewfinder.mts} (89%) rename scripts/internal/{release-notes.mjs => release-notes.mts} (83%) rename scripts/internal/{set-react-version.mjs => set-react-version.mts} (86%) rename scripts/internal/{test.mjs => test.mts} (79%) rename scripts/testing/{react-native.mjs => react-native.mts} (89%) rename scripts/testing/{test-apple.mjs => test-apple.mts} (88%) rename scripts/testing/{test-e2e.mjs => test-e2e.mts} (87%) rename scripts/testing/{test-matrix.mjs => test-matrix.mts} (87%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 175963fc9..55b3c2e2c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -239,7 +239,7 @@ jobs: - name: react-native run-ios if: ${{ steps.affected.outputs.ios != '' }} run: | - ../scripts/testing/react-native.mjs run-ios + ../scripts/testing/react-native.mts run-ios working-directory: template-example timeout-minutes: 60 android: @@ -326,7 +326,7 @@ jobs: - name: react-native run-android if: ${{ steps.affected.outputs.android != '' }} run: | - ../scripts/testing/react-native.mjs run-android + ../scripts/testing/react-native.mts run-android working-directory: template-example timeout-minutes: 60 macos: diff --git a/android/app/src/main/java/com/microsoft/reacttestapp/manifest/Manifest.kt b/android/app/src/main/java/com/microsoft/reacttestapp/manifest/Manifest.kt index 0625fd672..ab5abff65 100644 --- a/android/app/src/main/java/com/microsoft/reacttestapp/manifest/Manifest.kt +++ b/android/app/src/main/java/com/microsoft/reacttestapp/manifest/Manifest.kt @@ -1,4 +1,4 @@ -// This file was generated by generate-manifest.mjs. +// This file was generated by generate-manifest.mts. // DO NOT MODIFY. ALL CHANGES WILL BE OVERWRITTEN. @file:Suppress("ktlint:standard:trailing-comma-on-declaration-site") diff --git a/eslint.config.js b/eslint.config.js index 4d4615bd9..056cbb3de 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -29,16 +29,16 @@ module.exports = [ }, { files: [ - "scripts/internal/generate-manifest-docs.mjs", - "scripts/internal/generate-manifest.mjs", - "scripts/internal/generate-schema.mjs", - "scripts/internal/pack.mjs", - "scripts/internal/set-react-version.mjs", - "scripts/internal/test.mjs", + "scripts/internal/generate-manifest-docs.mts", + "scripts/internal/generate-manifest.mts", + "scripts/internal/generate-schema.mts", + "scripts/internal/pack.mts", + "scripts/internal/set-react-version.mts", + "scripts/internal/test.mts", "scripts/schema.mjs", - "scripts/testing/test-apple.mjs", - "scripts/testing/test-e2e.mjs", - "scripts/testing/test-matrix.mjs", + "scripts/testing/test-apple.mts", + "scripts/testing/test-e2e.mts", + "scripts/testing/test-matrix.mts", "scripts/utils/colors.mjs", ], rules: { diff --git a/ios/ReactTestApp/Manifest.swift b/ios/ReactTestApp/Manifest.swift index 33f0392fd..52a5299f1 100644 --- a/ios/ReactTestApp/Manifest.swift +++ b/ios/ReactTestApp/Manifest.swift @@ -1,4 +1,4 @@ -// This file was generated by generate-manifest.mjs. +// This file was generated by generate-manifest.mts. // DO NOT MODIFY. ALL CHANGES WILL BE OVERWRITTEN. struct Component { diff --git a/package.json b/package.json index 2ba0f2f8d..144e924b1 100644 --- a/package.json +++ b/package.json @@ -67,24 +67,24 @@ }, "scripts": { "format:c": "clang-format -i $(git ls-files '*.cpp' '*.h' '*.m' '*.mm')", - "format:js": "prettier --write --log-level error $(git ls-files '*.[cm]js' '*.[jt]s' '*.tsx' '*.yml' 'CONTRIBUTING.md' 'README.md' 'test/**/*.json' ':!:.yarn/releases')", + "format:js": "prettier --write --log-level error $(git ls-files '*.[cm][jt]s' '*.[jt]s' '*.tsx' '*.yml' 'CONTRIBUTING.md' 'README.md' 'test/**/*.json' ':!:.yarn/releases')", "format:swift": "swiftformat $(git ls-files '*.swift')", - "generate:code": "node scripts/internal/generate-manifest.mjs", - "generate:docs": "node scripts/internal/generate-manifest-docs.mjs", - "generate:schema": "node scripts/internal/generate-schema.mjs", + "generate:code": "node --experimental-transform-types --no-warnings scripts/internal/generate-manifest.mts", + "generate:docs": "node --experimental-transform-types --no-warnings scripts/internal/generate-manifest-docs.mts", + "generate:schema": "node --experimental-transform-types --no-warnings scripts/internal/generate-schema.mts", "lint:commit": "git log --format='%s' origin/trunk..HEAD | tail -1 | npx @rnx-kit/commitlint-lite@2.0.0", - "lint:js": "eslint $(git ls-files '*.[cm]js' '*.[jt]s' '*.tsx' ':!:*.config.js' ':!:.yarn/releases') && tsc && tsc --project tsconfig.cjs.json", + "lint:js": "eslint $(git ls-files '*.[cm][jt]s' '*.[jt]s' '*.tsx' ':!:*.config.js' ':!:.yarn/releases') && tsc && tsc --project tsconfig.cjs.json", "lint:kt": "ktlint --relative 'android/app/src/**/*.kt'", "lint:rb": "bundle exec rubocop", "lint:swift": "swiftlint", - "prepack": "node scripts/internal/pack.mjs pre", - "postpack": "node scripts/internal/pack.mjs post", - "release-notes": "node scripts/internal/release-notes.mjs", - "set-react-version": "node scripts/internal/set-react-version.mjs", - "show-affected": "node --experimental-transform-types --no-warnings scripts/build/affected.ts", - "test": "node scripts/internal/test.mjs", + "prepack": "node --experimental-transform-types --no-warnings scripts/internal/pack.mts pre", + "postpack": "node --experimental-transform-types --no-warnings scripts/internal/pack.mts post", + "release-notes": "node --experimental-transform-types --no-warnings scripts/internal/release-notes.mts", + "set-react-version": "node --experimental-transform-types --no-warnings scripts/internal/set-react-version.mts", + "show-affected": "node --experimental-transform-types --no-warnings scripts/build/affected.mts", + "test": "node --experimental-transform-types --no-warnings scripts/internal/test.mts", "test:js": "node --experimental-transform-types --no-warnings --test $(git ls-files '*.test.ts')", - "test:matrix": "node scripts/testing/test-matrix.mjs", + "test:matrix": "node --experimental-transform-types --no-warnings scripts/testing/test-matrix.mts", "test:rb": "bundle exec ruby -Ilib:test -e \"Dir.glob('./test/test_*.rb').each { |file| require(file) }\"" }, "dependencies": { diff --git a/scripts/build/affected.ts b/scripts/build/affected.mts similarity index 100% rename from scripts/build/affected.ts rename to scripts/build/affected.mts diff --git a/scripts/internal/generate-manifest-docs.mjs b/scripts/internal/generate-manifest-docs.mts similarity index 86% rename from scripts/internal/generate-manifest-docs.mjs rename to scripts/internal/generate-manifest-docs.mts index 27fe20c79..612bf6a22 100644 --- a/scripts/internal/generate-manifest-docs.mjs +++ b/scripts/internal/generate-manifest-docs.mts @@ -1,6 +1,7 @@ -// @ts-check +// eslint-disable-next-line no-restricted-imports +import type { SchemaObject } from "ajv"; import * as path from "node:path"; -import { readDocumentation } from "./generate-schema.mjs"; +import { assertDefinition, readDocumentation } from "./generate-schema.mts"; import { generateSchema } from "../schema.mjs"; async function generateManifestDocs() { @@ -9,12 +10,13 @@ async function generateManifestDocs() { /** * Renders the specified JSON object schema. - * @param {import("ajv").SchemaObject} definition - * @param {string[]} toc - * @param {string[]} lines - * @param {string} scope */ - const render = (definition, toc, lines, scope = "") => { + const render = ( + definition: SchemaObject, + toc: string[], + lines: string[], + scope = "" + ) => { if (Array.isArray(definition.allOf)) { for (const { $ref } of definition.allOf) { render(schema.$defs[$ref.replace("#/$defs/", "")], toc, lines, scope); @@ -37,6 +39,8 @@ async function generateManifestDocs() { })(); for (const [key, def] of Object.entries(definition.properties)) { + assertDefinition(def); + const { description, markdownDescription, type } = def; const text = markdownDescription || description; if (!text) { diff --git a/scripts/internal/generate-manifest.mjs b/scripts/internal/generate-manifest.mts similarity index 85% rename from scripts/internal/generate-manifest.mjs rename to scripts/internal/generate-manifest.mts index 833d387e1..9b6076c5e 100644 --- a/scripts/internal/generate-manifest.mjs +++ b/scripts/internal/generate-manifest.mts @@ -1,13 +1,25 @@ -// @ts-check +// eslint-disable-next-line no-restricted-imports +import type { SchemaObject } from "ajv"; import * as fs from "node:fs/promises"; import * as path from "node:path"; import { URL, fileURLToPath } from "node:url"; import { generateSchema } from "../schema.mjs"; +import { assertDefinition } from "./generate-schema.mts"; + +type Language = { + options: { + indent: string; + level: number; + footer?: string; + header?: string; + }; + arrayProperty: (name: string, type: string, required: boolean) => string; + objectProperty: (name: string, required: boolean) => string; + stringProperty: (name: string, required: boolean) => string; + structBegin: (name: string) => string; + structEnd: string; +}; -/** - * @import { SchemaObject } from "ajv"; - * @import { Language } from "../types.js"; - */ const thisScript = fileURLToPath(import.meta.url); /** @@ -15,7 +27,7 @@ const thisScript = fileURLToPath(import.meta.url); * @param {string} ref * @returns {string} */ -function typename(ref) { +function typename(ref: string): string { const i = ref.lastIndexOf("/") + 1; return ref[i].toUpperCase() + ref.substring(i + 1); } @@ -25,11 +37,10 @@ function typename(ref) { * @param {string} output * @returns {Language} */ -function getLanguage(output) { +function getLanguage(output: string): Language { switch (path.extname(output)) { case ".h": { - /** @type {(type: string, required: boolean) => string} */ - const nullable = (type, required) => + const nullable = (type: string, required: boolean) => required ? type : `std::optional<${type}>`; return { options: { @@ -80,8 +91,7 @@ function getLanguage(output) { } case ".kt": { - /** @type {(required: boolean) => "" | "?"} */ - const nullable = (required) => (required ? "" : "?"); + const nullable = (required: boolean) => (required ? "" : "?"); return { options: { indent: " ", @@ -110,8 +120,7 @@ function getLanguage(output) { } case ".swift": { - /** @type {(required: boolean) => "" | "?"} */ - const nullable = (required) => (required ? "" : "?"); + const nullable = (required: boolean) => (required ? "" : "?"); return { options: { indent: " ", @@ -152,12 +161,12 @@ function getLanguage(output) { /** * Generates a data model from the specified schema definition. - * @param {string} name - * @param {SchemaObject} definition - * @param {Language} lang - * @returns {string[]} */ -function generateType(name, definition, lang) { +function generateType( + name: string, + definition: SchemaObject, + lang: Language +): string[] { const { indent, level } = lang.options; const outer = indent.repeat(level); const inner = indent.repeat(level + 1); @@ -166,6 +175,8 @@ function generateType(name, definition, lang) { const { properties, required = [] } = definition; Object.entries(properties).forEach(([name, prop]) => { + assertDefinition(prop); + const isRequired = required.includes(name); switch (prop.type) { case "array": @@ -188,10 +199,8 @@ function generateType(name, definition, lang) { /** * Generates manifest data models and writes them to specified path. - * @param {SchemaObject} schema - * @param {string} output */ -async function generate(schema, output) { +async function generate(schema: SchemaObject, output: string) { const lang = getLanguage(output); const lines = [ `// This file was generated by ${path.basename(thisScript)}.`, @@ -204,15 +213,9 @@ async function generate(schema, output) { } Object.entries(schema.$defs).forEach(([key, definition]) => { + assertDefinition(definition); if (!("exclude-from-codegen" in definition)) { - lines.push( - ...generateType( - typename(key), - /** @type {SchemaObject} */ (definition), - lang - ), - "" - ); + lines.push(...generateType(typename(key), definition, lang), ""); } return lines; }); diff --git a/scripts/internal/generate-schema.mjs b/scripts/internal/generate-schema.mts similarity index 70% rename from scripts/internal/generate-schema.mjs rename to scripts/internal/generate-schema.mts index f32e5eb30..5ba3b64f2 100644 --- a/scripts/internal/generate-schema.mjs +++ b/scripts/internal/generate-schema.mts @@ -1,27 +1,37 @@ -// @ts-check +// eslint-disable-next-line no-restricted-imports +import type { SchemaObject } from "ajv"; import * as fs from "node:fs/promises"; import * as os from "node:os"; import * as path from "node:path"; import { URL, fileURLToPath } from "node:url"; import { isMain } from "../helpers.js"; import { generateSchema } from "../schema.mjs"; +import type { Docs } from "../types.js"; -/** @import { Docs } from "../types.js"; */ +type Definition = SchemaObject & { + type: string; + description: string; + markdownDescription?: string; +}; -/** @type {(str: string) => string} */ -const stripCarriageReturn = +const stripCarriageReturn: (str: string) => string = os.EOL === "\r\n" ? (str) => str.replaceAll("\r", "") : (str) => str; -/** - * @returns {Promise>} - */ -export async function readDocumentation() { - /** @type {Partial} */ - const docs = {}; +export function assertDefinition(props: unknown): asserts props is Definition { + if ( + !props || + typeof props !== "object" || + !("type" in props || "allOf" in props || "oneOf" in props) + ) { + throw new Error(`Invalid definition in schema: ${JSON.stringify(props)}`); + } +} + +export async function readDocumentation(): Promise> { + const docs: Partial = {}; const docsDir = fileURLToPath(new URL("../../docs", import.meta.url)); - /** @type {(keyof Docs)[]} */ - const keys = [ + const keys: (keyof Docs)[] = [ "introduction", "bundleRoot", "components", @@ -67,6 +77,7 @@ if (isMain(import.meta.url)) { .then((docs) => generateSchema(docs)) .then((schema) => { for (const def of Object.values(schema.$defs)) { + assertDefinition(def); delete def["exclude-from-codegen"]; } return stripCarriageReturn(JSON.stringify(schema, undefined, 2)) + "\n"; diff --git a/scripts/internal/pack.mjs b/scripts/internal/pack.mts similarity index 91% rename from scripts/internal/pack.mjs rename to scripts/internal/pack.mts index 590cce94c..68818d679 100644 --- a/scripts/internal/pack.mjs +++ b/scripts/internal/pack.mts @@ -1,4 +1,3 @@ -// @ts-check import * as fs from "node:fs"; import * as path from "node:path"; @@ -6,10 +5,8 @@ const files = ["example/.gitignore", "example/windows/.gitignore"]; /** * Renames `.dotfile` to `_dotfile`. - * @param {string} p - * @returns {string} */ -function renameDotFile(p) { +function renameDotFile(p: string): string { return path.join(path.dirname(p), "_" + path.basename(p).substring(1)); } diff --git a/scripts/internal/prepare-viewfinder.mjs b/scripts/internal/prepare-viewfinder.mts similarity index 89% rename from scripts/internal/prepare-viewfinder.mjs rename to scripts/internal/prepare-viewfinder.mts index 294dd0f95..67d7611b3 100755 --- a/scripts/internal/prepare-viewfinder.mjs +++ b/scripts/internal/prepare-viewfinder.mts @@ -1,5 +1,4 @@ -#!/usr/bin/env node -// @ts-check +#!/usr/bin/env -S node --experimental-transform-types --no-warnings import { spawnSync } from "node:child_process"; import * as fs from "node:fs"; @@ -40,11 +39,8 @@ function configureAppManifest() { /** * Runs the specified command. - * @param {string} command - * @param {string[]} args - * @param {Record=} options */ -function $(command, args, options) { +function $(command: string, args: string[], options?: Record) { const { error, status } = spawnSync(command, args, { cwd: PROJECT_ROOT, stdio: "inherit", diff --git a/scripts/internal/release-notes.mjs b/scripts/internal/release-notes.mts similarity index 83% rename from scripts/internal/release-notes.mjs rename to scripts/internal/release-notes.mts index 5bd446a43..b5f78033e 100644 --- a/scripts/internal/release-notes.mjs +++ b/scripts/internal/release-notes.mts @@ -1,25 +1,20 @@ /** * This script is only used to help write release announcements. */ -// @ts-check // @ts-expect-error Could not find a declaration file for module import { generateNotes } from "@semantic-release/release-notes-generator"; import { spawn } from "node:child_process"; import * as path from "node:path"; import { URL, fileURLToPath } from "node:url"; import { readJSONFile } from "../helpers.js"; +import type { Manifest } from "../types.js"; -/** @typedef {import("../types.js").Manifest} Manifest */ - -/** - * @param {string} output - * @param {string} lastRelease - * @param {string} nextRelease - * @returns {string} - */ -function reformat(output, lastRelease, nextRelease) { - /** @type {[RegExp, string][]} */ - const replacements = [ +function reformat( + output: string, + lastRelease: string, + nextRelease: string +): string { + const replacements: [RegExp, string][] = [ [/^# .*/m, `📣 react-native-test-app ${nextRelease}`], [/^### .*/m, `Other fixes since ${lastRelease}:`], [/^\* \*\*android:\*\*/gm, "* **Android:**"], @@ -40,7 +35,7 @@ function reformat(output, lastRelease, nextRelease) { function repositoryUrl() { const p = fileURLToPath(new URL("../../package.json", import.meta.url)); - const manifest = /** @type {Manifest} */ (readJSONFile(p)); + const manifest = readJSONFile(p); return manifest.repository?.url; } @@ -48,7 +43,7 @@ function repositoryUrl() { * @param {string} lastRelease * @param {string} nextRelease */ -function main(lastRelease, nextRelease) { +function main(lastRelease: string, nextRelease: string): void { const args = [ "log", `--pretty=format:{ "hash": "%H", "message": "%s" }`, @@ -86,8 +81,7 @@ function main(lastRelease, nextRelease) { cwd: process.cwd(), }; - /** @type {Promise} */ - const releaseNotes = generateNotes({}, context); + const releaseNotes: Promise = generateNotes({}, context); releaseNotes .then((output) => reformat(output, lastRelease, nextRelease)) .then((output) => console.log(output)); diff --git a/scripts/internal/set-react-version.mjs b/scripts/internal/set-react-version.mts similarity index 86% rename from scripts/internal/set-react-version.mjs rename to scripts/internal/set-react-version.mts index 25a6c7656..f93690759 100644 --- a/scripts/internal/set-react-version.mjs +++ b/scripts/internal/set-react-version.mts @@ -1,5 +1,3 @@ -// @ts-check - /** * Reminder that this script is meant to be runnable without installing * dependencies. It can therefore not rely on any external libraries. @@ -15,42 +13,30 @@ import { toVersionNumber, v, } from "../helpers.js"; +import type { Manifest } from "../types.js"; import { fetchPackageMetadata, npmRegistryBaseURL } from "../utils/npm.mjs"; -/** @import { Manifest } from "../types.js"; */ -/** - * @template T - * @typedef {{ [P in keyof T]: Required> }} RequiredObject - */ - const VALID_TAGS = ["canary-macos", "canary-windows", "nightly"]; /** * Returns whether specified string is a valid version number. - * @param {string} v - * @return {boolean} */ -function isValidVersion(v) { +function isValidVersion(v: string): boolean { return /^\d+\.\d+$/.test(v) || VALID_TAGS.includes(v); } /** * Type-safe `Object.keys()` - * @template {Record} T - * @param {T} obj - * @returns {(keyof T)[]} */ -function keys(obj) { - return /** @type {(keyof T)[]} */ (Object.keys(obj)); +function keys>(obj: T): (keyof T)[] { + return Object.keys(obj) as (keyof T)[]; } -/** - * @param {string} filename - * @param {string | RegExp} searchValue - * @param {string} replaceValue - * @returns {Promise} - */ -function searchReplaceInFile(filename, searchValue, replaceValue) { +function searchReplaceInFile( + filename: string, + searchValue: string | RegExp, + replaceValue: string +): Promise { const current = readTextFile(filename); const updated = current.replace(searchValue, replaceValue); return updated === current @@ -83,10 +69,12 @@ function disableWebStorage() { /** * Infer the React Native version an out-of-tree platform package is based on. - * @param {Manifest} manifest - * @returns {string} */ -function inferReactNativeVersion({ name, version, dependencies = {} }) { +function inferReactNativeVersion({ + name, + version, + dependencies = {}, +}: Manifest): string { const codegenVersion = dependencies["@react-native/codegen"]; if (!codegenVersion) { throw new Error( @@ -102,11 +90,8 @@ function inferReactNativeVersion({ name, version, dependencies = {} }) { /** * Fetches package information. - * @param {string} pkg - * @param {string} version - * @return {Promise} */ -function fetchPackageInfo(pkg, version) { +function fetchPackageInfo(pkg: string, version: string): Promise { return fetchPackageMetadata(pkg) .then(({ ["dist-tags"]: distTags, versions }) => { const tags = [version, version + "-stable", "v" + version + "-stable"]; @@ -131,7 +116,7 @@ function fetchPackageInfo(pkg, version) { } return fetch(npmRegistryBaseURL + pkg + "/" + foundVersion); }) - .then((res) => res?.json() ?? /** @type {Manifest} */ ({})) + .then((res) => res?.json() ?? ({} as Manifest)) .then(({ version, dependencies = {}, peerDependencies = {} }) => { return { version, dependencies, peerDependencies }; }); @@ -139,9 +124,8 @@ function fetchPackageInfo(pkg, version) { /** * Fetches the latest react-native-windows@canary information via NuGet. - * @return {Promise} */ -function fetchReactNativeWindowsCanaryInfoViaNuGet() { +function fetchReactNativeWindowsCanaryInfoViaNuGet(): Promise { const rnwNuGetFeed = "https://pkgs.dev.azure.com/ms/react-native/_packaging/react-native-public/nuget/v3/index.json"; return fetch(rnwNuGetFeed) @@ -188,14 +172,11 @@ function fetchReactNativeWindowsCanaryInfoViaNuGet() { /** * Returns an object with common dependencies. - * @param {string} v - * @param {Manifest} manifest - * @return {Promise>} */ async function resolveCommonDependencies( - v, - { dependencies = {}, peerDependencies = {} } -) { + v: string, + { dependencies = {}, peerDependencies = {} }: Manifest +): Promise> { const [rnBabelPresetVersion, rnMetroConfigVersion, metroBabelPresetVersion] = await (async () => { if (["^", "canary", "nightly"].some((tag) => v.includes(tag))) { @@ -263,12 +244,12 @@ async function resolveCommonDependencies( /** * Returns a profile for specified version. - * @param {string} v - * @param {boolean} coreOnly - * @return {Promise>} */ -async function getProfile(v, coreOnly) { - const manifest = /** @type {Manifest} */ (readJSONFile("package.json")); +async function getProfile( + v: string, + coreOnly: boolean +): Promise> { + const manifest = readJSONFile("package.json"); const visionos = manifest.defaultPlatformPackages?.["visionos"]; if (!visionos) { throw new Error("Missing platform package for visionOS"); @@ -329,8 +310,7 @@ async function getProfile(v, coreOnly) { const reactNative = await versions.core; const commonDeps = await resolveCommonDependencies(v, reactNative); - /** @type {(manifest: Manifest) => string | undefined} */ - const getVersion = ({ version }) => version; + const getVersion = ({ version }: Manifest) => version; return { ...commonDeps, "react-native": reactNative.version, @@ -344,21 +324,20 @@ async function getProfile(v, coreOnly) { /** * Sets specified React Native version. - * @param {string} version - * @param {boolean} coreOnly - * @param {Record} [overrides] - * @return {Promise} */ -export async function setReactVersion(version, coreOnly, overrides = {}) { - /** @type {fs.FileHandle | undefined} */ - let fd; +export async function setReactVersion( + version: string, + coreOnly: boolean, + overrides: Record = {} +): Promise { + let fd: fs.FileHandle | undefined; try { const profile = { ...(await getProfile(version, coreOnly)), ...overrides }; console.dir(profile, { depth: null }); const manifests = ["package.json", "example/package.json"]; for (const manifestPath of manifests) { - const manifest = /** @type {Manifest} */ (readJSONFile(manifestPath)); + const manifest = readJSONFile(manifestPath); const { dependencies, devDependencies, resolutions = {} } = manifest; if (!devDependencies) { throw new Error("Expected 'devDependencies' to be declared"); @@ -424,8 +403,7 @@ if (isMain(import.meta.url)) { ); process.exitCode = 1; } else { - const { "core-only": coreOnly, overrides } = - /** @type {RequiredObject} */ (values); + const { "core-only": coreOnly, overrides } = values; setReactVersion(version, coreOnly, JSON.parse(overrides)).then(() => { const numVersion = VALID_TAGS.includes(version) ? Number.MAX_SAFE_INTEGER diff --git a/scripts/internal/test.mjs b/scripts/internal/test.mts similarity index 79% rename from scripts/internal/test.mjs rename to scripts/internal/test.mts index 31ce67d6a..6c66107c9 100644 --- a/scripts/internal/test.mjs +++ b/scripts/internal/test.mts @@ -1,11 +1,8 @@ -// @ts-check import { spawnSync } from "node:child_process"; -/** - * @param {string[]} files - * @returns {"ruby" | "typescript" | undefined} - */ -function getTarget(files) { +type Language = "ruby" | "typescript"; + +function getTarget(files: string[]): Language | undefined { if (files.some((file) => file.endsWith(".rb"))) { return "ruby"; } else if (files.some((file) => file.endsWith(".ts"))) { @@ -15,11 +12,7 @@ function getTarget(files) { } } -/** - * @param {string} command - * @param {string[]} args - */ -function testWith(command, args) { +function testWith(command: string, args: string[]): void { const result = spawnSync(command, args, { stdio: "inherit" }); process.exitCode = result.status ?? 1; } diff --git a/scripts/testing/react-native.mjs b/scripts/testing/react-native.mts similarity index 89% rename from scripts/testing/react-native.mjs rename to scripts/testing/react-native.mts index 503944bfe..2fa216c60 100755 --- a/scripts/testing/react-native.mjs +++ b/scripts/testing/react-native.mts @@ -1,15 +1,13 @@ -#!/usr/bin/env node -// @ts-check +#!/usr/bin/env -S node --experimental-transform-types --no-warnings + import { spawnSync } from "node:child_process"; const DEVICE_ID = "T-800"; /** * Runs the specified command. - * @param {RegExp} successPattern The string to look for - * @param {...any} args The command to run and its arguments */ -function run(successPattern, ...args) { +function run(successPattern: RegExp, ...args: readonly string[]) { const { stderr } = spawnSync("yarn", args, { encoding: "utf-8" }); if (!successPattern.test(stderr)) { throw new Error(stderr); diff --git a/scripts/testing/test-apple.mjs b/scripts/testing/test-apple.mts similarity index 88% rename from scripts/testing/test-apple.mjs rename to scripts/testing/test-apple.mts index 187d3d125..5f31e2181 100644 --- a/scripts/testing/test-apple.mjs +++ b/scripts/testing/test-apple.mts @@ -1,5 +1,3 @@ -// @ts-check - /** * Reminder that this script is meant to be runnable without installing * dependencies. It can therefore not rely on any external libraries. @@ -8,9 +6,8 @@ import { spawnSync } from "node:child_process"; import * as fs from "node:fs"; import { URL, fileURLToPath } from "node:url"; import { memo, readTextFile } from "../helpers.js"; -import { $ } from "./test-e2e.mjs"; - -/** @import { BuildConfig } from "../types.js"; */ +import type { BuildConfig } from "../types.js"; +import { $ } from "./test-e2e.mts"; export const getIOSSimulatorName = memo(() => { const wdioConfig = new URL( @@ -39,10 +36,12 @@ export const getIOSSimulatorName = memo(() => { /** * Configures `Podfile` and invokes `pod install`. - * @param {Required} config - * @returns {Promise} */ -export function installPods({ platform, engine, variant }) { +export function installPods({ + platform, + engine, + variant, +}: Required): Promise { const podfile = `${platform}/Podfile`; let content = readTextFile(podfile); diff --git a/scripts/testing/test-e2e.mjs b/scripts/testing/test-e2e.mts similarity index 87% rename from scripts/testing/test-e2e.mjs rename to scripts/testing/test-e2e.mts index 5f2bc422c..f8f073d3e 100644 --- a/scripts/testing/test-e2e.mjs +++ b/scripts/testing/test-e2e.mts @@ -1,5 +1,3 @@ -// @ts-check - /** * Reminder that this script is meant to be runnable without installing * dependencies. It can therefore not rely on any external libraries. @@ -10,10 +8,8 @@ import { isMain } from "../helpers.js"; /** * Invokes a shell command with optional arguments. - * @param {string} command - * @param {...string} args */ -export function $(command, ...args) { +export function $(command: string, ...args: string[]) { const { status } = spawnSync(command, args, { stdio: "inherit" }); if (status !== 0) { throw new Error( @@ -25,11 +21,8 @@ export function $(command, ...args) { /** * Invokes a shell command with optional arguments. Similar {@link $}, but * captures and returns stdout/stderr. - * @param {string} command - * @param {...string} args - * @returns {string} */ -export function $$(command, ...args) { +export function $$(command: string, ...args: string[]): string { const { status, stderr, stdout } = spawnSync(command, args, { stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8", @@ -45,15 +38,13 @@ export function $$(command, ...args) { /** * Ensures Appium is available. - * @returns {Promise} */ -function ensureAppiumAvailable() { +function ensureAppiumAvailable(): Promise { return new Promise((resolve, reject) => { const socket = new Socket(); socket.setTimeout(60 * 1000); - /** @type {(e: Error) => void} */ - const onError = (e) => { + const onError = (e: Error) => { socket.destroy(); reject(new Error(`Could not connect to Appium server: ${e}`)); }; @@ -95,11 +86,7 @@ function prepareAndroid(androidHome = process.env["ANDROID_HOME"]) { $(adb, "install", "android/app/build/outputs/apk/debug/app-debug.apk"); } -/** - * @param {string} target - * @param {string[]=} args - */ -export async function test(target, args = []) { +export async function test(target: string, args: string[] = []) { switch (target) { case "android": prepareAndroid(); diff --git a/scripts/testing/test-matrix.mjs b/scripts/testing/test-matrix.mts similarity index 87% rename from scripts/testing/test-matrix.mjs rename to scripts/testing/test-matrix.mts index b3e553ee3..abcde4b4d 100644 --- a/scripts/testing/test-matrix.mjs +++ b/scripts/testing/test-matrix.mts @@ -1,5 +1,3 @@ -// @ts-check - /** * Reminder that this script is meant to be runnable without installing * dependencies. It can therefore not rely on any external libraries. @@ -9,20 +7,23 @@ import * as fs from "node:fs"; import { URL, fileURLToPath } from "node:url"; import * as util from "node:util"; import { readTextFile } from "../helpers.js"; -import { setReactVersion } from "../internal/set-react-version.mjs"; +import { setReactVersion } from "../internal/set-react-version.mts"; +import type { BuildConfig, TargetPlatform } from "../types.js"; import { green, red, yellow } from "../utils/colors.mjs"; -import { getIOSSimulatorName, installPods } from "./test-apple.mjs"; -import { $, $$, test } from "./test-e2e.mjs"; +import { getIOSSimulatorName, installPods } from "./test-apple.mts"; +import { $, $$, test } from "./test-e2e.mts"; -/** - * @import { BuildConfig, PlatformConfig, TargetPlatform } from "../types.js"; - */ +type PlatformConfig = { + name: string; + engines: ReadonlyArray<"hermes" | "jsc">; + isAvailable: (config: Required) => boolean; + prebuild: (config: Required) => Promise; +}; const DEFAULT_PLATFORMS = ["android", "ios"]; -const TEST_VARIANTS = /** @type {const} */ (["paper", "fabric"]); +const TEST_VARIANTS = ["paper", "fabric"] as const; -/** @type {Record} */ -const PLATFORM_CONFIG = { +const PLATFORM_CONFIG: Record = { android: { name: "Android", engines: ["hermes"], @@ -76,10 +77,8 @@ function log(message = "", tag = TAG) { /** * Invokes `npm run` and redirects stdout/stderr to specified file. - * @param {string} script - * @param {string} logPath */ -function run(script, logPath) { +function run(script: string, logPath: string) { const fd = fs.openSync(logPath, "a", 0o644); const proc = spawn(PACKAGE_MANAGER, ["run", script], { stdio: ["ignore", fd, fd], @@ -87,10 +86,7 @@ function run(script, logPath) { return proc; } -/** - * @param {string} message - */ -function showBanner(message) { +function showBanner(message: string) { log(); log(message, "┗━━▶"); log("", ""); @@ -112,13 +108,8 @@ function startDevServer(logPath = "metro.server.log") { return run("start", logPath); } -/** - * @param {string[]} platforms - * @returns {TargetPlatform[]} - */ -function validatePlatforms(platforms) { - /** @type {TargetPlatform[]} */ - const filtered = []; +function validatePlatforms(platforms: string[]): TargetPlatform[] { + const filtered: TargetPlatform[] = []; for (const platform of platforms) { switch (platform) { case "android": @@ -140,10 +131,7 @@ function validatePlatforms(platforms) { return filtered; } -/** - * @param {string[]} args - */ -function parseArgs(args) { +function parseArgs(args: string[]) { const { values, positionals } = util.parseArgs({ args, options: { @@ -200,9 +188,8 @@ function prestart() { /** * Invokes `react-native run-`. - * @param {TargetPlatform} platform */ -function buildAndRun(platform) { +function buildAndRun(platform: TargetPlatform) { switch (platform) { case "ios": { const simulator = getIOSSimulatorName(); @@ -216,10 +203,7 @@ function buildAndRun(platform) { } } -/** - * @param {BuildConfig} config - */ -async function buildRunTest({ platform, variant }) { +async function buildRunTest({ platform, variant }: BuildConfig) { const setup = PLATFORM_CONFIG[platform]; if (!setup) { log(yellow(`⚠ Unknown platform: ${platform}`)); @@ -239,10 +223,7 @@ async function buildRunTest({ platform, variant }) { } } -/** - * @param {string} rootDir - */ -function reset(rootDir) { +function reset(rootDir: string) { log("Resetting..."); process.chdir(rootDir); @@ -266,10 +247,11 @@ function reset(rootDir) { /** * Invokes callback within the context of specified React Native version. - * @param {string} version - * @param {() => Promise} action */ -async function withReactNativeVersion(version, action) { +async function withReactNativeVersion( + version: string, + action: () => Promise +) { reset(rootDir); if (version) { diff --git a/scripts/types.ts b/scripts/types.ts index 7ddec26d4..e8e155da4 100644 --- a/scripts/types.ts +++ b/scripts/types.ts @@ -192,24 +192,6 @@ export type AppManifest = { }; }; -/************************* - * generate-manifest.mjs * - *************************/ - -export type Language = { - options: { - indent: string; - level: number; - footer?: string; - header?: string; - }; - arrayProperty: (name: string, type: string, required: boolean) => string; - objectProperty: (name: string, required: boolean) => string; - stringProperty: (name: string, required: boolean) => string; - structBegin: (name: string) => string; - structEnd: string; -}; - /************** * schema.mjs * **************/ @@ -262,9 +244,9 @@ export type Manifest = Partial<{ defaultPlatformPackages: Record; }>; -/************************* - * test-matrix.mjs * - *************************/ +/*************************** + * testing/test-matrix.mts * + ***************************/ export type ApplePlatform = "ios" | "macos" | "visionos"; export type TargetPlatform = ApplePlatform | "android" | "windows"; @@ -274,10 +256,3 @@ export type BuildConfig = { variant: "fabric" | "paper"; engine?: "hermes" | "jsc"; }; - -export type PlatformConfig = { - name: string; - engines: ReadonlyArray<"hermes" | "jsc">; - isAvailable: (config: Required) => boolean; - prebuild: (config: Required) => Promise; -}; diff --git a/test/configure/updatePackageManifest.test.ts b/test/configure/updatePackageManifest.test.ts index 0f70c9ad6..e6a09fb55 100644 --- a/test/configure/updatePackageManifest.test.ts +++ b/test/configure/updatePackageManifest.test.ts @@ -8,8 +8,8 @@ import { fs, setMockFiles } from "../fs.mock.ts"; function getExampleManifest() { const p = new URL("../../example/package.json", import.meta.url); - const manifest = readJSONFile(p); - return manifest as Manifest; + const manifest = readJSONFile(p); + return manifest; } describe("updatePackageManifest()", () => { diff --git a/tsconfig.json b/tsconfig.json index 2bb989614..7a2637809 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,6 +10,7 @@ }, "include": [ "**/*.mjs", + "**/*.mts", "**/*.ts" ] } diff --git a/windows/Shared/Manifest.h b/windows/Shared/Manifest.h index 568749634..c78b0dca8 100644 --- a/windows/Shared/Manifest.h +++ b/windows/Shared/Manifest.h @@ -1,4 +1,4 @@ -// This file was generated by generate-manifest.mjs. +// This file was generated by generate-manifest.mts. // DO NOT MODIFY. ALL CHANGES WILL BE OVERWRITTEN. #pragma once