Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(gatsby): type exposed workerpool functions #31768

Merged
merged 2 commits into from
Jun 4, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/gatsby/src/bootstrap/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { Runner, createGraphQLRunner } from "./create-graphql-runner"
import reporter from "gatsby-cli/lib/reporter"
import { globalTracer } from "opentracing"
import JestWorker from "jest-worker"
import type { GatsbyWorkerPool } from "../utils/worker/pool"
import { handleStalePageData } from "../utils/page-data"

const tracer = globalTracer()
Expand All @@ -23,7 +23,7 @@ export async function bootstrap(
initialContext: Partial<IBuildContext>
): Promise<{
gatsbyNodeGraphQLFunction: Runner
workerPool: JestWorker
workerPool: GatsbyWorkerPool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fancy! 🍷

}> {
const spanArgs = initialContext.parentSpan
? { childOf: initialContext.parentSpan }
Expand Down
15 changes: 8 additions & 7 deletions packages/gatsby/src/commands/build-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import * as buildUtils from "./build-utils"
import { Span } from "opentracing"
import { IProgram, Stage } from "./types"
import { PackageJson } from "../.."
import type { GatsbyWorkerPool } from "../utils/worker/pool"

type IActivity = any // TODO
type IWorkerPool = any // TODO

export interface IBuildArgs extends IProgram {
directory: string
Expand Down Expand Up @@ -193,15 +193,15 @@ export interface IRenderHtmlResult {
}

const renderHTMLQueue = async (
workerPool: IWorkerPool,
workerPool: GatsbyWorkerPool,
activity: IActivity,
htmlComponentRendererPath: string,
pages: Array<string>,
stage: Stage = Stage.BuildHTML
): Promise<void> => {
// We need to only pass env vars that are set programmatically in gatsby-cli
// to child process. Other vars will be picked up from environment.
const envVars = [
const envVars: Array<[string, string | undefined]> = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this be undefined, or is this because of process.env having the undefined type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just process.env.X will be string | undefined - so it technically could undefined if it wasn't setup before. We could force TS into making those just string but then it's not really checking for type safety 🤷

[`NODE_ENV`, process.env.NODE_ENV],
[`gatsby_executing_command`, process.env.gatsby_executing_command],
[`gatsby_log_level`, process.env.gatsby_log_level],
Expand All @@ -220,14 +220,15 @@ const renderHTMLQueue = async (

try {
await Bluebird.map(segments, async pageSegment => {
const htmlRenderMeta: IRenderHtmlResult = await renderHTML({
const renderHTMLResult = await renderHTML({
envVars,
htmlComponentRendererPath,
paths: pageSegment,
sessionId,
})

if (stage === `build-html`) {
const htmlRenderMeta = renderHTMLResult as IRenderHtmlResult
store.dispatch({
type: `HTML_GENERATED`,
payload: pageSegment,
Expand Down Expand Up @@ -304,7 +305,7 @@ export const doBuildPages = async (
rendererPath: string,
pagePaths: Array<string>,
activity: IActivity,
workerPool: IWorkerPool,
workerPool: GatsbyWorkerPool,
stage: Stage
): Promise<void> => {
try {
Expand Down Expand Up @@ -332,7 +333,7 @@ export const buildHTML = async ({
stage: Stage
pagePaths: Array<string>
activity: IActivity
workerPool: IWorkerPool
workerPool: GatsbyWorkerPool
}): Promise<void> => {
const { rendererPath } = await buildRenderer(program, stage, activity.span)
await doBuildPages(rendererPath, pagePaths, activity, workerPool, stage)
Expand All @@ -346,7 +347,7 @@ export async function buildHTMLPagesAndDeleteStaleArtifacts({
program,
}: {
pageRenderer: string
workerPool: IWorkerPool
workerPool: GatsbyWorkerPool
buildSpan?: Span
program: IBuildArgs
}): Promise<{
Expand Down
3 changes: 1 addition & 2 deletions packages/gatsby/src/services/initialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { getBrowsersList } from "../utils/browserslist"
import { Store, AnyAction } from "redux"
import { preferDefault } from "../bootstrap/prefer-default"
import * as WorkerPool from "../utils/worker/pool"
import JestWorker from "jest-worker"
import { startPluginRunner } from "../redux/plugin-runner"
import { loadPlugins } from "../bootstrap/load-plugins"
import { store, emitter } from "../redux"
Expand Down Expand Up @@ -76,7 +75,7 @@ export async function initialize({
parentSpan,
}: IBuildContext): Promise<{
store: Store<IGatsbyState, AnyAction>
workerPool: JestWorker
workerPool: WorkerPool.GatsbyWorkerPool
}> {
if (process.env.GATSBY_DISABLE_CACHE_PERSISTENCE) {
reporter.info(
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby/src/services/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { GraphQLRunner } from "../query/graphql-runner"
import { Store, AnyAction } from "redux"
import { IGatsbyState } from "../redux/types"
import { Express } from "express"
import JestWorker from "jest-worker"
import type { GatsbyWorkerPool } from "../utils/worker/pool"
import { Actor, AnyEventObject } from "xstate"
import { Compiler } from "webpack"
import { WebsocketManager } from "../utils/websocket-manager"
Expand All @@ -30,7 +30,7 @@ export interface IBuildContext {
webhookBody?: Record<string, unknown>
webhookSourcePluginName?: string
refresh?: boolean
workerPool?: JestWorker
workerPool?: GatsbyWorkerPool
app?: Express
nodesMutatedDuringQueryRun?: boolean
nodesMutatedDuringQueryRunRecompileCount?: number
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby/src/state-machines/data-layer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Runner } from "../../bootstrap/create-graphql-runner"
import { GraphQLRunner } from "../../query/graphql-runner"
import { Store, AnyAction } from "redux"
import { IGatsbyState } from "../../redux/types"
import JestWorker from "jest-worker"
import type { GatsbyWorkerPool } from "../../utils/worker/pool"
export interface IGroupedQueryIds {
pageQueryIds: Array<string>
staticQueryIds: Array<string>
Expand All @@ -25,7 +25,7 @@ export interface IDataLayerContext {
webhookBody?: Record<string, unknown>
webhookSourcePluginName?: string
refresh?: boolean
workerPool?: JestWorker
workerPool?: GatsbyWorkerPool
pagesToBuild?: Array<string>
pagesToDelete?: Array<string>
shouldRunCreatePagesStatefully?: boolean
Expand Down
5 changes: 2 additions & 3 deletions packages/gatsby/src/utils/start-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import { Express } from "express"
import * as path from "path"

import { Stage, IProgram } from "../commands/types"
import JestWorker from "jest-worker"
import { findOriginalSourcePositionAndContent } from "./stack-trace-utils"
import { appendPreloadHeaders } from "./develop-preload-headers"
import {
Expand All @@ -58,7 +57,7 @@ interface IServer {
webpackActivity: ActivityTracker
cancelDevJSNotice: CancelExperimentNoticeCallbackOrUndefined
websocketManager: WebsocketManager
workerPool: JestWorker
workerPool: WorkerPool.GatsbyWorkerPool
webpackWatching: IWebpackWatchingPauseResume
}

Expand All @@ -70,7 +69,7 @@ export interface IWebpackWatchingPauseResume {
export async function startServer(
program: IProgram,
app: Express,
workerPool: JestWorker = WorkerPool.create()
workerPool: WorkerPool.GatsbyWorkerPool = WorkerPool.create()
): Promise<IServer> {
const directory = program.directory

Expand Down
12 changes: 10 additions & 2 deletions packages/gatsby/src/utils/worker/pool.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import Worker from "jest-worker"
import { cpuCoreCount } from "gatsby-core-utils"

export const create = (): Worker =>
// we only import it to get types, typescript will remove it from code if it's only used for types
import * as exposedWorkerPoolMethods from "./child"
import type { CreateWorkerPoolType } from "./types"

export type GatsbyWorkerPool = CreateWorkerPoolType<
typeof exposedWorkerPoolMethods
>

export const create = (): GatsbyWorkerPool =>
new Worker(require.resolve(`./child`), {
numWorkers: Math.max(1, cpuCoreCount() - 1),
forkOptions: {
silent: false,
},
})
}) as GatsbyWorkerPool
4 changes: 2 additions & 2 deletions packages/gatsby/src/utils/worker/render-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ export const renderHTMLProd = async ({
}: {
htmlComponentRendererPath: string
paths: Array<string>
envVars: Array<Array<string>>
envVars: Array<[string, string | undefined]>
sessionId: number
}): Promise<IRenderHtmlResult> => {
const publicDir = join(process.cwd(), `public`)
Expand Down Expand Up @@ -353,7 +353,7 @@ export const renderHTMLDev = async ({
}: {
htmlComponentRendererPath: string
paths: Array<string>
envVars: Array<Array<string>>
envVars: Array<[string, string | undefined]>
sessionId: number
}): Promise<Array<unknown>> => {
const outputDir = join(process.cwd(), `.cache`, `develop-html`)
Expand Down
24 changes: 24 additions & 0 deletions packages/gatsby/src/utils/worker/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type Worker from "jest-worker"

type WrapReturnOfAFunctionInAPromise<
FunctionThatDoesNotReturnAPromise extends (...args: Array<any>) => any
> = (
...a: Parameters<FunctionThatDoesNotReturnAPromise>
) => Promise<ReturnType<FunctionThatDoesNotReturnAPromise>>

// jest-worker will make sync function async, so to keep proper types we need to adjust types so all functions
// on worker pool are async
type EnsureFunctionReturnsAPromise<MaybeFunction> = MaybeFunction extends (
...args: Array<any>
) => Promise<any>
? MaybeFunction
: MaybeFunction extends (...args: Array<any>) => any
? WrapReturnOfAFunctionInAPromise<MaybeFunction>
: never
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

never to disallow non-functions if export is not a function

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is complex XD isn't there another way to accomplish the same or not really?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My TS-foo is not strong, so not sure about simpler way (at least to have those types automatically) - there might, I just don't know of any

I initially just used exported types from child module as they are (the way jest itself does it - https://github.com/facebook/jest/blob/5f4dd187d89070d07617444186684c20d9213031/packages/jest-runner/src/index.ts#L38-L40), but if you have sync method in child - using it on workerPool would return promise anyway, so types were not always correct.

I guess simple approach would be to define types of "exposed functions" manually, but then it's possibly error-prone and easy to get out of sync.


export type CreateWorkerPoolType<ExposedFunctions> = Worker &
{
[FunctionName in keyof ExposedFunctions]: EnsureFunctionReturnsAPromise<
ExposedFunctions[FunctionName]
>
}
Comment on lines +19 to +24
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WorkerPool type creator, now used just for main workerPool, but in following PRs will be used for tests where test workerPool will include additional helper functions for tests