Skip to content

Commit 669c52e

Browse files
authored
typechecking inputs/outputs in action transform tests (#75935)
This PR attempts to add typechecking to the server actions tests fixtures in order to add an extra layer of validation against compiler bugs. (there's a couple!). Unfortunately the tests were pretty loosey-goosey around undefined variables, so there's a lot of noise in adding missing imports for `Button` etc. i tried to call out the interesting changes via PR comments. Note that this is a bit limited, because we often have to rely on typescript inferring some sensible types for the outputs -- see e.g. the issue with `24/output.js` mentioned below. In case typescript can't infer sensible types for something, we can just exclude that file from checking. this seems to be pretty rare, so i think it's okay. We should also definitely add typechecking to the other fixture folders, but this PR is already big, so i want to do that piecemeal. ### TODO - [x] invoke the typechecker in CI - added a `check-compiler-fixtures` script that runs as part of `types-and-precompiled` (open to feedback, maybe this isn't the right place?) - [x] figure out how to make `tsc` error on `crates/next-custom-transforms/tests/fixture/server-actions/server-graph/51/output.js`, where we're referencing a non-existent variable `$$RSC_SERVER_ACTION_0`. right now, it emits no errors - this is a compiler bug, and i want to catch bugs like this in the future - solution: #75944 - [x] figure out why `tsc` is complaining about a type mismatch in `crates/next-custom-transforms/tests/fixture/server-actions/server-graph/24/output.js` - we can work around this by loosening the types for `registerServerReference`, but i'd rather we didn't have to do that - this seems to be a bizarre TS behavior where `fn.bind(...)` makes all the params optional in JS files. ignoring this for now
1 parent 871ca31 commit 669c52e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+292
-53
lines changed
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use server'
22

3-
export const { sampleFunction } = someObject
4-
export const { sampleFunction2 } = fn()
5-
export let { 0: sampleFunction3, 1: sampleFunction4 } = [g(), g()]
3+
import ANYTHING from 'anything'
4+
5+
export const { sampleFunction } = ANYTHING
6+
export const { sampleFunction2 } = ANYTHING()
7+
export let { 0: sampleFunction3, 1: sampleFunction4 } = [ANYTHING(), ANYTHING()]

crates/next-custom-transforms/tests/fixture/server-actions/client-graph/5/input.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export async function action0(b, c, ...g) {
66
return async function action1(d) {
77
'use server'
88
let f
9+
// @ts-expect-error: window is not iterable
910
console.log(...window, { window })
1011
console.log(a, b, action2)
1112

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/// <reference types="./next" />
2+
/// <reference types="./modules" />
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
declare module 'database' {
2+
export const db: {
3+
serialize<T>(cb: () => T): T
4+
run(
5+
statement: string,
6+
args: Record<string, any>,
7+
onComplete: (this: { lastID: string }, error?: Error) => void
8+
): void
9+
}
10+
}
11+
12+
declare module 'db' {
13+
export default function deleteFromDb(arg: any, ...args: any[]): Promise<void>
14+
}
15+
16+
declare module 'auth' {
17+
export function validator<TFn extends (...args: any[]) => any>(fn: TFn): TFn
18+
export function another<TFn extends (...args: any[]) => any>(fn: TFn): TFn
19+
}
20+
21+
declare module 'anything' {
22+
const ANYTHING: any
23+
export default ANYTHING
24+
}
25+
26+
declare module 'foo' {
27+
const f: any
28+
export default f
29+
export const f1: any
30+
export const f2: any
31+
}
32+
33+
declare module 'components' {
34+
import React from 'react'
35+
36+
export function Button(
37+
props: {
38+
action: () => Promise<any>
39+
} & React.ComponentProps<'button'>
40+
): React.ReactNode
41+
42+
export function Form(
43+
props: React.PropsWithChildren<{ action: () => Promise<any> }>
44+
): React.ReactNode
45+
46+
export function Client(props: Record<string, any>): React.ReactNode
47+
}
48+
49+
declare module 'navigation' {
50+
export function redirect(href: string): void
51+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
declare module 'private-next-rsc-action-encryption' {
2+
export function encryptActionBoundArgs(
3+
actionId: string,
4+
...args: any[]
5+
): Promise<string>
6+
7+
export function decryptActionBoundArgs(
8+
actionId: string,
9+
encryptedPromise: Promise<string>
10+
): Promise<any[]>
11+
}
12+
13+
declare module 'private-next-rsc-server-reference' {
14+
export function registerServerReference<T extends (...args: any[]) => any>(
15+
reference: T,
16+
id: string,
17+
exportName: string | null
18+
): T
19+
}
20+
21+
declare module 'private-next-rsc-action-client-wrapper' {
22+
export function callServer(
23+
actionId: string,
24+
actionArgs: unknown[]
25+
): Promise<unknown>
26+
27+
export function findSourceMapURL(filename: string): string | null
28+
29+
const createServerReference: (
30+
id: string,
31+
callServer: any,
32+
encodeFormAction?: any,
33+
findSourceMapURL?: any,
34+
functionName?: string
35+
) => (...args: unknown[]) => Promise<unknown>
36+
}
37+
38+
declare module 'private-next-rsc-action-validate' {
39+
function ensureServerEntryExports(actions: unknown[]): void
40+
}
41+
42+
declare module 'private-next-rsc-cache-wrapper' {
43+
export function cache<TFn extends (...args: any[]) => Promise<any>>(
44+
kind: string,
45+
id: string,
46+
boundArgsLength: number,
47+
fn: TFn
48+
): TFn
49+
}

crates/next-custom-transforms/tests/fixture/server-actions/server-graph/1/input.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Button } from 'components'
12
import deleteFromDb from 'db'
23

34
export function Item({ id1, id2 }) {

crates/next-custom-transforms/tests/fixture/server-actions/server-graph/1/output.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* __next_internal_action_entry_do_not_use__ {"406a88810ecce4a4e8b59d53b8327d7e98bbf251d7":"$$RSC_SERVER_ACTION_0","4090b5db271335765a4b0eab01f044b381b5ebd5cd":"$$RSC_SERVER_ACTION_1"} */ import { registerServerReference } from "private-next-rsc-server-reference";
22
import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption";
3+
import { Button } from 'components';
34
import deleteFromDb from 'db';
45
export const /*#__TURBOPACK_DISABLE_EXPORT_MERGING__*/ $$RSC_SERVER_ACTION_0 = async function deleteItem($$ACTION_CLOSURE_BOUND) {
56
var [$$ACTION_ARG_0, $$ACTION_ARG_1] = await decryptActionBoundArgs("406a88810ecce4a4e8b59d53b8327d7e98bbf251d7", $$ACTION_CLOSURE_BOUND);

crates/next-custom-transforms/tests/fixture/server-actions/server-graph/16/input.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Button } from 'components'
12
import deleteFromDb from 'db'
23

34
const v1 = 'v1'
@@ -13,7 +14,7 @@ export function Item({ id1, id2 }) {
1314
return <Button action={deleteItem}>Delete</Button>
1415
}
1516

16-
const f = (x) => {
17+
let f = (x) => {
1718
async function g(y, ...z) {
1819
'use server'
1920
return x + y + z[0]
@@ -23,6 +24,14 @@ const f = (x) => {
2324
const g = (x) => {
2425
f = async (y, ...z) => {
2526
'use server'
26-
return x + y + z[0]
27+
return (
28+
x +
29+
y +
30+
// can't be a `ts-expect-error` because the type of `z` changes to `any` in the output
31+
// and it stops being an error
32+
//
33+
// @ts-ignore: incompatible argument types
34+
z[0]
35+
)
2736
}
2837
}

crates/next-custom-transforms/tests/fixture/server-actions/server-graph/16/output.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* __next_internal_action_entry_do_not_use__ {"406a88810ecce4a4e8b59d53b8327d7e98bbf251d7":"$$RSC_SERVER_ACTION_0","7f1c36b06e398c97abe5d5d7ae8c672bfddf4e1b91":"$$RSC_SERVER_ACTION_2","7f90b5db271335765a4b0eab01f044b381b5ebd5cd":"$$RSC_SERVER_ACTION_1"} */ import { registerServerReference } from "private-next-rsc-server-reference";
22
import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption";
3+
import { Button } from 'components';
34
import deleteFromDb from 'db';
45
const v1 = 'v1';
56
export const /*#__TURBOPACK_DISABLE_EXPORT_MERGING__*/ $$RSC_SERVER_ACTION_0 = async function deleteItem($$ACTION_CLOSURE_BOUND) {
@@ -17,12 +18,16 @@ export const /*#__TURBOPACK_DISABLE_EXPORT_MERGING__*/ $$RSC_SERVER_ACTION_1 = a
1718
var [$$ACTION_ARG_0] = await decryptActionBoundArgs("7f90b5db271335765a4b0eab01f044b381b5ebd5cd", $$ACTION_CLOSURE_BOUND);
1819
return $$ACTION_ARG_0 + y + z[0];
1920
};
20-
const f = (x)=>{
21+
let f = (x)=>{
2122
var g = registerServerReference($$RSC_SERVER_ACTION_1, "7f90b5db271335765a4b0eab01f044b381b5ebd5cd", null).bind(null, encryptActionBoundArgs("7f90b5db271335765a4b0eab01f044b381b5ebd5cd", x));
2223
};
2324
export const /*#__TURBOPACK_DISABLE_EXPORT_MERGING__*/ $$RSC_SERVER_ACTION_2 = async function f($$ACTION_CLOSURE_BOUND, y, ...z) {
2425
var [$$ACTION_ARG_0] = await decryptActionBoundArgs("7f1c36b06e398c97abe5d5d7ae8c672bfddf4e1b91", $$ACTION_CLOSURE_BOUND);
25-
return $$ACTION_ARG_0 + y + z[0];
26+
return $$ACTION_ARG_0 + y + // can't be a `ts-expect-error` because the type of `z` changes to `any` in the output
27+
// and it stops being an error
28+
//
29+
// @ts-ignore: incompatible argument types
30+
z[0];
2631
};
2732
const g = (x)=>{
2833
f = registerServerReference($$RSC_SERVER_ACTION_2, "7f1c36b06e398c97abe5d5d7ae8c672bfddf4e1b91", null).bind(null, encryptActionBoundArgs("7f1c36b06e398c97abe5d5d7ae8c672bfddf4e1b91", x));

crates/next-custom-transforms/tests/fixture/server-actions/server-graph/18/input.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Button } from 'components'
12
import deleteFromDb from 'db'
23

34
const v1 = 'v1'

0 commit comments

Comments
 (0)