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: pin xstate #36398

Merged
merged 3 commits into from
Aug 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/gatsby-source-filesystem/package.json
Expand Up @@ -16,7 +16,7 @@
"mime": "^2.5.2",
"pretty-bytes": "^5.4.1",
"valid-url": "^1.0.9",
"xstate": "^4.26.1"
"xstate": "4.32.1"
},
"devDependencies": {
"@babel/cli": "^7.15.4",
Expand Down Expand Up @@ -47,4 +47,4 @@
"engines": {
"node": ">=14.15.0"
}
}
}
2 changes: 1 addition & 1 deletion packages/gatsby/package.json
Expand Up @@ -169,7 +169,7 @@
"webpack-merge": "^5.8.0",
"webpack-stats-plugin": "^1.0.3",
"webpack-virtual-modules": "^0.3.2",
"xstate": "^4.26.0",
"xstate": "4.32.1",
"yaml-loader": "^0.6.0"
},
"devDependencies": {
Expand Down
200 changes: 200 additions & 0 deletions packages/gatsby/src/state-machines/__tests__/query-running.ts
@@ -0,0 +1,200 @@
import { queryRunningMachine } from "../query-running"
import { queryActions } from "../query-running/actions"
import { interpret, Interpreter } from "xstate"
import { IProgram } from "../../commands/types"
import { store } from "../../redux"
import reporter from "gatsby-cli/lib/reporter"
import pDefer from "p-defer"
import { IGroupedQueryIds } from "../../services/types"

const services = {
extractQueries: jest.fn(async () => {}),
writeOutRequires: jest.fn(async () => {}),
calculateDirtyQueries: jest.fn(
async (): Promise<{
queryIds: IGroupedQueryIds
}> => {
return {
queryIds: {
pageQueryIds: [],
staticQueryIds: [],
},
}
}
),
}

const machine = queryRunningMachine.withConfig(
{
actions: queryActions,
services,
},
{
program: {} as IProgram,
store,
reporter,
pendingQueryRuns: new Set([`/`]),
}
)

const resetMocks = (mocks: Record<string, jest.Mock>): void =>
Object.values(mocks).forEach(mock => mock.mockClear())

const resetAllMocks = (): void => {
resetMocks(services)
}

const finished = async (
service: Interpreter<any, any, any, any, any>
): Promise<void> =>
new Promise(resolve => {
service.onDone(() => resolve())
})

function debug(service: Interpreter<any, any, any, any, any>): void {
let last: any

service.onTransition(state => {
if (!last) {
last = state
} else if (!state.changed) {
return
}

reporter.info(
`---onTransition---\n${require(`util`).inspect(
{
stateValue: state.value,
event: state.event,
pendingQueryRuns: state.context.pendingQueryRuns,
changedStateValue: state.value !== last.value,
},
{ depth: Infinity }
)}`
)
last = state
})
}

expect.extend({
toHaveInSet(received, item) {
if (received.has(item)) {
return {
pass: true,
message: (): string =>
`Expected ${Array.from(received)} not to contain ${item}`,
}
} else {
return {
pass: false,
message: (): string =>
`Expected ${Array.from(received)} not to contain ${item}`,
}
}
},
})

/* eslint-disable @typescript-eslint/no-namespace */
declare global {
namespace jest {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface Expect {
toHaveInSet(item: any): any
}
}
}

describe(`query-running state machine`, () => {
beforeEach(() => {
resetAllMocks()
})

it(`initialises`, async () => {
const service = interpret(machine)
// debug(service)

service.start()
expect(service.state.value).toBe(`extractingQueries`)
})

it(`doesn't drop pendingQueryRuns that were added during calculation of dirty queries`, async () => {
const deferred = pDefer<{
queryIds: IGroupedQueryIds
}>()
const waitForExecutionOfCalcDirtyQueries = pDefer()

services.calculateDirtyQueries.mockImplementation(
async (): Promise<{
queryIds: IGroupedQueryIds
}> => {
waitForExecutionOfCalcDirtyQueries.resolve()

// allow test to execute some code before resuming service

await deferred.promise

return {
queryIds: {
pageQueryIds: [],
staticQueryIds: [],
},
}
}
)

const service = interpret(machine)
// debug(service)

service.send({
type: `QUERY_RUN_REQUESTED`,
payload: {
pagePath: `/bar/`,
},
})

service.start()

await waitForExecutionOfCalcDirtyQueries.promise

// we are in middle of execution of calcDirtyQueries service
// let's dispatch QUERY_RUN_REQUESTED for page /foo/
service.send({
type: `QUERY_RUN_REQUESTED`,
payload: {
pagePath: `/foo/`,
},
})

deferred.resolve()

// let state machine reach final state
await finished(service)

// let's make sure that we called calculateDirtyQueries service
// with every page that was requested, even if page was requested
// while we were executing calcDirtyQueries service
expect(services.calculateDirtyQueries).toHaveBeenCalledWith(
expect.objectContaining({
currentlyHandledPendingQueryRuns: expect.toHaveInSet(`/`),
}),
expect.anything(),
expect.anything()
)

expect(services.calculateDirtyQueries).toHaveBeenCalledWith(
expect.objectContaining({
currentlyHandledPendingQueryRuns: expect.toHaveInSet(`/bar/`),
}),
expect.anything(),
expect.anything()
)

expect(services.calculateDirtyQueries).toHaveBeenCalledWith(
expect.objectContaining({
currentlyHandledPendingQueryRuns: expect.toHaveInSet(`/foo/`),
}),
expect.anything(),
expect.anything()
)
})
})
10 changes: 5 additions & 5 deletions packages/gatsby/src/state-machines/data-layer/services.ts
@@ -1,4 +1,4 @@
import { ServiceConfig } from "xstate"
import { MachineOptions } from "xstate"
import {
customizeSchema,
createPages,
Expand All @@ -8,10 +8,10 @@ import {
} from "../../services"
import { IDataLayerContext } from "./types"

export const dataLayerServices: Record<
string,
ServiceConfig<IDataLayerContext>
> = {
export const dataLayerServices: MachineOptions<
IDataLayerContext,
any
>["services"] = {
customizeSchema,
sourceNodes,
createPages,
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby/src/state-machines/develop/services.ts
Expand Up @@ -13,9 +13,9 @@ import {
} from "../data-layer"
import { queryRunningMachine } from "../query-running"
import { waitingMachine } from "../waiting"
import { ServiceConfig } from "xstate"
import { MachineOptions } from "xstate"

export const developServices: Record<string, ServiceConfig<IBuildContext>> = {
export const developServices: MachineOptions<IBuildContext, any>["services"] = {
initializeData: initializeDataMachine,
reloadData: reloadDataMachine,
recreatePages: recreatePagesMachine,
Expand Down
10 changes: 5 additions & 5 deletions packages/gatsby/src/state-machines/query-running/services.ts
@@ -1,4 +1,4 @@
import { ServiceConfig } from "xstate"
import { MachineOptions } from "xstate"
import {
extractQueries,
writeOutRequires,
Expand All @@ -10,10 +10,10 @@ import {
} from "../../services"
import { IQueryRunningContext } from "./types"

export const queryRunningServices: Record<
string,
ServiceConfig<IQueryRunningContext>
> = {
export const queryRunningServices: MachineOptions<
IQueryRunningContext,
any
>["services"] = {
extractQueries,
writeOutRequires,
calculateDirtyQueries,
Expand Down
4 changes: 3 additions & 1 deletion packages/gatsby/src/utils/state-machine-logging.ts
Expand Up @@ -7,12 +7,14 @@ import {
} from "xstate"
import reporter from "gatsby-cli/lib/reporter"

type AnyInterpreterWithContext<T> = Interpreter<T, any, any, any, any>

const isInterpreter = <T>(
actor: Actor<T> | Interpreter<T>
): actor is Interpreter<T> => `machine` in actor

export function logTransitions<T = DefaultContext>(
service: Interpreter<T>
service: AnyInterpreterWithContext<T>
): void {
const listeners = new WeakSet()
let last: State<T, AnyEventObject, any, any>
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Expand Up @@ -26950,10 +26950,10 @@ xss@^1.0.6:
commander "^2.20.3"
cssfilter "0.0.10"

xstate@^4.26.0, xstate@^4.26.1:
version "4.26.1"
resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.26.1.tgz#4fc1afd153f88cf302a9ee2b758f6629e6a829b6"
integrity sha512-JLofAEnN26l/1vbODgsDa+Phqa61PwDlxWu8+2pK+YbXf+y9pQSDLRvcYH2H1kkeUBA5fGp+xFL/zfE8jNMw4g==
xstate@4.32.1:
version "4.32.1"
resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.32.1.tgz#1a09c808a66072938861a3b4acc5b38460244b70"
integrity sha512-QYUd+3GkXZ8i6qdixnOn28bL3EvA++LONYL/EMWwKlFSh/hiLndJ8YTnz77FDs+JUXcwU7NZJg7qoezoRHc4GQ==

xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1:
version "4.0.2"
Expand Down