From 0299cc927328b33d50956774b45f75f5a40fefeb Mon Sep 17 00:00:00 2001 From: Jeff Yates Date: Mon, 3 Apr 2023 14:07:27 -0500 Subject: [PATCH] =?UTF-8?q?[=F0=9F=94=A5AUDIT=F0=9F=94=A5]=20[rg.audit.1]?= =?UTF-8?q?=20Add=20custom=20flow=20file=20for=20render-environment-jsdom?= =?UTF-8?q?=20to=20work=20around=20flowgen=20issue=20(#616)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🖍 _This is an audit!_ 🖍 ## Summary: - Add the faster flowgen script from Wonder Blocks, and include the "custom overrides" support. - Fix some flow type issues for core and render-environment-jsdom. Issue: FEI-4960 ## Test plan: `yarn clean && yarn build && yarn build:types && yarn build:flowtypes` Also check that the types will work for the usages I have to hand. Author: somewhatabstract Auditors: somewhatabstract, #typescript, #frontend-infra Required Reviewers: Approved By: Checks: ⌛ Lint, typecheck, and coverage check (ubuntu-latest, 16.x), ✅ gerald, ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 16.x), ⌛ Analyze (javascript), ⏭ dependabot Pull Request URL: https://github.com/Khan/wonder-stuff/pull/616 --- .changeset/eleven-trains-rush.md | 6 + build-scripts/gen-flow-types.ts | 57 +++++++-- packages/wonder-stuff-core/src/types.js.flow | 34 ++++++ .../src/types.js.flow | 111 ++++++++++++++++++ 4 files changed, 198 insertions(+), 10 deletions(-) create mode 100644 .changeset/eleven-trains-rush.md create mode 100644 packages/wonder-stuff-core/src/types.js.flow create mode 100644 packages/wonder-stuff-render-environment-jsdom/src/types.js.flow diff --git a/.changeset/eleven-trains-rush.md b/.changeset/eleven-trains-rush.md new file mode 100644 index 00000000..3ea26eea --- /dev/null +++ b/.changeset/eleven-trains-rush.md @@ -0,0 +1,6 @@ +--- +"@khanacademy/wonder-stuff-render-environment-jsdom": patch +"@khanacademy/wonder-stuff-core": patch +--- + +Fix some errors in the generated flow types diff --git a/build-scripts/gen-flow-types.ts b/build-scripts/gen-flow-types.ts index dfa5192b..4d0a4cd4 100644 --- a/build-scripts/gen-flow-types.ts +++ b/build-scripts/gen-flow-types.ts @@ -1,33 +1,70 @@ -import {execFileSync} from "child_process"; import * as fs from "fs"; import * as path from "path"; import * as fglob from "fast-glob"; +import {compiler, beautify} from "flowgen"; const rootDir = path.join(__dirname, ".."); const files = fglob.sync("packages/wonder-stuff-*/dist/**/*.d.ts", { cwd: rootDir, }); +if (files.length) { + console.log(`found ${files.length} files`); +} else { + throw new Error("no typescript type definitions found"); +} + +// The node API for `flowgen` doesn't have an option to add a flow header +// so we need to do it ourselves. +const HEADER = `/** + * Flowtype definitions for data + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.21.0 + * @flow + */ +`; + +let errorCount = 0; for (const inFile of files) { const outFile = inFile.replace(".d.ts", ".js.flow"); - const args = ["flowgen", inFile, "-o", outFile, "--add-flow-header"]; + + const overrideFile = outFile.replace("/dist/", "/src/"); + if (fs.existsSync(overrideFile)) { + console.log(`copying\nfrom: ${overrideFile}\nto: ${outFile}`); + fs.cpSync( + path.join(rootDir, overrideFile), + path.join(rootDir, outFile), + ); + continue; + } try { - execFileSync("yarn", args, {cwd: rootDir}); - console.log(`✅ wrote: ${outFile}`); + const source = fs.readFileSync(path.join(rootDir, inFile), "utf-8"); + const options = {inexact: false}; + let contents: string = compiler.compileDefinitionString( + source, + options, + ); + contents = beautify(contents); // `flowgen` sometimes outputs code that uses `mixins` instead of `extends` // so we do some post-processing on the files to clean that up. - const contents = fs.readFileSync(path.join(rootDir, outFile), "utf-8"); if (contents.includes(" mixins ")) { + contents = contents.replace(/ mixins /g, " extends "); console.log("replacing 'mixins' with 'extends'"); - fs.writeFileSync( - path.join(rootDir, outFile), - contents.replace(/ mixins /g, " extends "), - "utf-8", - ); } + + fs.writeFileSync( + path.join(rootDir, outFile), + HEADER + contents, + "utf-8", + ); + console.log(`✅ wrote: ${outFile}`); } catch (e) { + errorCount += 1; console.log(`❌ error processing: ${inFile}: ${e}`); } } + +// Fail the build step if there were any files we couldn't process +process.exit(errorCount); diff --git a/packages/wonder-stuff-core/src/types.js.flow b/packages/wonder-stuff-core/src/types.js.flow new file mode 100644 index 00000000..c95de2bd --- /dev/null +++ b/packages/wonder-stuff-core/src/types.js.flow @@ -0,0 +1,34 @@ +/** + * Flowtype definitions for data + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.21.0 + * @flow + */ +export type MetadataPrimitive = string | number | boolean | null | void; +export type MetadataArray = Array>; +/** + * A collection of data. + */ +export type Metadata = { + [name: string]: + | Metadata + | MetadataPrimitive + | MetadataArray, +}; +/** + * A secret that is a string. + * + * This opaque type makes it clearer when secrets are being used and enforces + * the need for explicit casting if they must be used as a string. + */ +export opaque type SecretString = string; +/** + * A collection of secrets keyed by their names. + */ +export type Secrets = { + +[key: string]: SecretString, +}; +/** + * Make a read-only type mutable. + */ +export type Mutable = $ObjMapi(P) => $ElementType>; diff --git a/packages/wonder-stuff-render-environment-jsdom/src/types.js.flow b/packages/wonder-stuff-render-environment-jsdom/src/types.js.flow new file mode 100644 index 00000000..e51f422f --- /dev/null +++ b/packages/wonder-stuff-render-environment-jsdom/src/types.js.flow @@ -0,0 +1,111 @@ +/** + * Flowtype definitions for types + * Generated by Flowgen from a Typescript Definition + * Flowgen v1.21.0 + * @flow + */ + +import type { ResourceLoader } from "jsdom"; +import type { + RenderAPI, + ICloseable, +} from "@khanacademy/wonder-stuff-render-server"; + +/** + * Gate API for control flow. + */ +export type IGate = { + /** + * Open the gate. + */ + open(): void, + + /** + * Close the gate. + */ + close: () => Promise | void, + + /** + * True, if the gate is open; otherwise, false. + */ + isOpen: boolean, + ... +} & ICloseable; + +/** + * Standard timer API as implemented by Node's global or a browser window. + */ +export interface ITimerAPI { + setTimeout: $PropertyType; + setInterval: $PropertyType; + requestAnimationFrame: $PropertyType; +} +/** + * A resource loader for use with JSDOM that can also have a close method for + * tidying up resources deterministically. + */ +export interface CloseableResourceLoader extends ICloseable, ResourceLoader { + /** + * Close the resource loader and tidy up resources. + * + * This is optional. + */ + +close?: () => void; +}; + +/** + * Configuration for a JSDOM environment. + */ +export interface IJSDOMConfiguration { + /** + * The name of the callback function that should be exposed by the + * environment for renderable code to use when registering for rendering. + */ + registrationCallbackName: string; + + /** + * Get a JSDOM resource loader for the given render request. + * @param {string} url The URL that is to be rendered. + * @param {RenderAPI} renderAPI An API of utilities for assisting with the + * render operation. + * @returns {CloseableResourceLoader} A ResourceLoader instance for use + * with JSDOM that can optionally have a close() method, which will be + * invoked when the render completes. + */ + getResourceLoader(url: string, renderAPI: RenderAPI): CloseableResourceLoader; + + /** + * Get the list of file URLs to retrieve and execute for the given request. + * @param {string} url The URL that is to be rendered. + * @param {RenderAPI} renderAPI An API of utilities for assisting with the + * render operation. + * @param {(url: string) => ?Promise} fetchFn Function to fetch a URL. Using this ensures proper tidy-up of associated + * sockets and agents. + * @returns {Promise>} An ordered array of absolute URLs for + * the JavaScript files that are to be executed. These are exectued in the + * same order as the array. + */ + getFileList( + url: string, + renderAPI: RenderAPI, + fetchFn: (url: string) => Promise | null | void + ): Promise>; + + /** + * Perform any additional environment setup. + * + * This method gets access to the actual environment in which code will + * execute. Be careful what you do. + * @param {string} url The URL that is to be rendered. + * @param {RenderAPI} renderAPI An API of utilities for assisting with the + * render operation. + * @param {any} vmContext The actual environment that is being setup. + * @returns {?Promise} A promise that the additional setup is done. + */ + afterEnvSetup( + url: string, + fileURLs: $ReadOnlyArray, + renderAPI: RenderAPI, + vmContext?: any + ): Promise; +}