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

Remove the experimental serverActions flag #57145

Merged
merged 5 commits into from
Oct 20, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,6 @@ With Server Actions, you don't need to manually create API endpoints. Instead, y
Server Actions can be defined in Server Components or called from Client Components. Defining the action in a Server Component allows the form to function without JavaScript, providing progressive enhancement.

Enable Server Actions in your `next.config.js` file:

```js filename="next.config.js"
module.exports = {
experimental: {
serverActions: true,
},
}
```

> **Good to know:**
>
> - Forms calling Server Actions from Server Components can function without JavaScript.
Expand Down
11 changes: 0 additions & 11 deletions docs/02-app/02-api-reference/04-functions/server-actions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,6 @@ Next.js integrates with React Actions to provide a built-in solution for [server

## Convention

You can enable Server Actions in your Next.js project by enabling the **experimental** `serverActions` flag.

```js filename="next.config.js"
module.exports = {
experimental: {
serverActions: true,
},
}
```

Server Actions can be defined in two places:

- Inside the component that uses it (Server Components only).
Expand Down Expand Up @@ -157,7 +147,6 @@ However, you can configure this limit using the `serverActionsBodySizeLimit` opt
```js filename="next.config.js"
module.exports = {
experimental: {
serverActions: true,
serverActionsBodySizeLimit: '2mb',
},
}
Expand Down
8 changes: 1 addition & 7 deletions packages/next/src/build/swc/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ function getBaseSWCOptions({
isServerLayer,
bundleTarget,
hasServerComponents,
isServerActionsEnabled,
}: {
filename: string
jest?: boolean
Expand All @@ -56,7 +55,6 @@ function getBaseSWCOptions({
modularizeImports?: NextConfig['modularizeImports']
compilerOptions: NextConfig['compiler']
swcPlugins: ExperimentalConfig['swcPlugins']
isServerActionsEnabled?: ExperimentalConfig['serverActions']
resolvedBaseUrl?: string
jsConfig: any
bundleTarget: BundleType
Expand Down Expand Up @@ -183,8 +181,7 @@ function getBaseSWCOptions({
serverActions:
hasServerComponents && !jest
? {
// TODO-APP: When Server Actions is stable, we need to remove this flag.
enabled: !!isServerActionsEnabled,
enabled: true,
isServer: !!isServerLayer,
}
: undefined,
Expand Down Expand Up @@ -320,7 +317,6 @@ export function getLoaderSWCOptions({
relativeFilePathFromRoot,
hasServerComponents,
isServerLayer,
isServerActionsEnabled,
bundleTarget,
}: // This is not passed yet as "paths" resolving is handled by webpack currently.
// resolvedBaseUrl,
Expand All @@ -346,7 +342,6 @@ export function getLoaderSWCOptions({
bundleTarget: BundleType
hasServerComponents?: boolean
isServerLayer: boolean
isServerActionsEnabled?: boolean
}) {
let baseOptions: any = getBaseSWCOptions({
filename,
Expand All @@ -361,7 +356,6 @@ export function getLoaderSWCOptions({
swcCacheDir,
hasServerComponents,
isServerLayer,
isServerActionsEnabled,
bundleTarget,
})
baseOptions.fontLoaders = {
Expand Down
3 changes: 0 additions & 3 deletions packages/next/src/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,6 @@ export default async function getBaseWebpackConfig(
const hasServerComponents = hasAppDir
const disableOptimizedLoading = true
const enableTypedRoutes = !!config.experimental.typedRoutes && hasAppDir
const useServerActions = !!config.experimental.serverActions && hasAppDir
const bundledReactChannel = needsExperimentalReact(config)
? '-experimental'
: ''
Expand Down Expand Up @@ -2080,7 +2079,6 @@ export default async function getBaseWebpackConfig(
appDir,
dev,
isEdgeServer,
useServerActions,
})),
hasAppDir &&
!isClient &&
Expand Down Expand Up @@ -2255,7 +2253,6 @@ export default async function getBaseWebpackConfig(
optimizeCss: config.experimental.optimizeCss,
nextScriptWorkers: config.experimental.nextScriptWorkers,
scrollRestoration: config.experimental.scrollRestoration,
serverActions: config.experimental.serverActions,
typedRoutes: config.experimental.typedRoutes,
basePath: config.basePath,
excludeDefaultMomentLocales: config.excludeDefaultMomentLocales,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ async function loaderTransform(
swcCacheDir,
relativeFilePathFromRoot,
hasServerComponents,
isServerActionsEnabled: nextConfig?.experimental?.serverActions,
isServerLayer,
bundleTarget,
})
Expand Down
208 changes: 99 additions & 109 deletions packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ interface Options {
dev: boolean
appDir: string
isEdgeServer: boolean
useServerActions: boolean
serverActionsBodySizeLimit?: SizeLimit
}

Expand Down Expand Up @@ -154,15 +153,13 @@ export class FlightClientEntryPlugin {
dev: boolean
appDir: string
isEdgeServer: boolean
useServerActions: boolean
serverActionsBodySizeLimit?: SizeLimit
assetPrefix: string

constructor(options: Options) {
this.dev = options.dev
this.appDir = options.appDir
this.isEdgeServer = options.isEdgeServer
this.useServerActions = options.useServerActions
this.serverActionsBodySizeLimit = options.serverActionsBodySizeLimit
this.assetPrefix = !this.dev && !this.isEdgeServer ? '../' : ''
}
Expand Down Expand Up @@ -387,78 +384,73 @@ export class FlightClientEntryPlugin {
)
}

if (this.useServerActions) {
compilation.hooks.finishModules.tapPromise(PLUGIN_NAME, () => {
const addedClientActionEntryList: Promise<any>[] = []
const actionMapsPerClientEntry: Record<
string,
Map<string, string[]>
> = {}

// We need to create extra action entries that are created from the
// client layer.
// Start from each entry's created SSR dependency from our previous step.
for (const [name, ssrEntryDepdendencies] of Object.entries(
createdSSRDependenciesForEntry
)) {
// Collect from all entries, e.g. layout.js, page.js, loading.js, ...
// add agregate them.
const actionEntryImports = this.collectClientActionsFromDependencies({
compilation,
dependencies: ssrEntryDepdendencies,
})
compilation.hooks.finishModules.tapPromise(PLUGIN_NAME, () => {
const addedClientActionEntryList: Promise<any>[] = []
const actionMapsPerClientEntry: Record<string, Map<string, string[]>> = {}

if (actionEntryImports.size > 0) {
if (!actionMapsPerClientEntry[name]) {
actionMapsPerClientEntry[name] = new Map()
}
actionMapsPerClientEntry[name] = new Map([
...actionMapsPerClientEntry[name],
...actionEntryImports,
])
// We need to create extra action entries that are created from the
// client layer.
// Start from each entry's created SSR dependency from our previous step.
for (const [name, ssrEntryDepdendencies] of Object.entries(
createdSSRDependenciesForEntry
)) {
// Collect from all entries, e.g. layout.js, page.js, loading.js, ...
// add agregate them.
const actionEntryImports = this.collectClientActionsFromDependencies({
compilation,
dependencies: ssrEntryDepdendencies,
})

if (actionEntryImports.size > 0) {
if (!actionMapsPerClientEntry[name]) {
actionMapsPerClientEntry[name] = new Map()
}
actionMapsPerClientEntry[name] = new Map([
...actionMapsPerClientEntry[name],
...actionEntryImports,
])
}
}

for (const [name, actionEntryImports] of Object.entries(
actionMapsPerClientEntry
)) {
// If an action method is already created in the server layer, we don't
// need to create it again in the action layer.
// This is to avoid duplicate action instances and make sure the module
// state is shared.
let remainingClientImportedActions = false
const remainingActionEntryImports = new Map<string, string[]>()
for (const [dep, actionNames] of actionEntryImports) {
const remainingActionNames = []
for (const actionName of actionNames) {
const id = name + '@' + dep + '@' + actionName
if (!createdActions.has(id)) {
remainingActionNames.push(actionName)
}
}
if (remainingActionNames.length > 0) {
remainingActionEntryImports.set(dep, remainingActionNames)
remainingClientImportedActions = true
for (const [name, actionEntryImports] of Object.entries(
actionMapsPerClientEntry
)) {
// If an action method is already created in the server layer, we don't
// need to create it again in the action layer.
// This is to avoid duplicate action instances and make sure the module
// state is shared.
let remainingClientImportedActions = false
const remainingActionEntryImports = new Map<string, string[]>()
for (const [dep, actionNames] of actionEntryImports) {
const remainingActionNames = []
for (const actionName of actionNames) {
const id = name + '@' + dep + '@' + actionName
if (!createdActions.has(id)) {
remainingActionNames.push(actionName)
}
}

if (remainingClientImportedActions) {
addedClientActionEntryList.push(
this.injectActionEntry({
compiler,
compilation,
actions: remainingActionEntryImports,
entryName: name,
bundlePath: name,
fromClient: true,
})
)
if (remainingActionNames.length > 0) {
remainingActionEntryImports.set(dep, remainingActionNames)
remainingClientImportedActions = true
}
}

return Promise.all(addedClientActionEntryList)
})
}
if (remainingClientImportedActions) {
addedClientActionEntryList.push(
this.injectActionEntry({
compiler,
compilation,
actions: remainingActionEntryImports,
entryName: name,
bundlePath: name,
fromClient: true,
})
)
}
}

return Promise.all(addedClientActionEntryList)
})

// Invalidate in development to trigger recompilation
const invalidator = getInvalidator(compiler.outputPath)
Expand Down Expand Up @@ -871,54 +863,52 @@ export class FlightClientEntryPlugin {
const serverActions: ActionManifest['node'] = {}
const edgeServerActions: ActionManifest['edge'] = {}

if (this.useServerActions) {
traverseModules(compilation, (mod, _chunk, chunkGroup, modId) => {
// Go through all action entries and record the module ID for each entry.
if (
chunkGroup.name &&
mod.request &&
/next-flight-action-entry-loader/.test(mod.request)
) {
const fromClient = /&__client_imported__=true/.test(mod.request)

const mapping = this.isEdgeServer
? pluginState.actionModEdgeServerId
: pluginState.actionModServerId

if (!mapping[chunkGroup.name]) {
mapping[chunkGroup.name] = {}
}
mapping[chunkGroup.name][fromClient ? 'client' : 'server'] = modId
traverseModules(compilation, (mod, _chunk, chunkGroup, modId) => {
// Go through all action entries and record the module ID for each entry.
if (
chunkGroup.name &&
mod.request &&
/next-flight-action-entry-loader/.test(mod.request)
) {
const fromClient = /&__client_imported__=true/.test(mod.request)

const mapping = this.isEdgeServer
? pluginState.actionModEdgeServerId
: pluginState.actionModServerId

if (!mapping[chunkGroup.name]) {
mapping[chunkGroup.name] = {}
}
})
mapping[chunkGroup.name][fromClient ? 'client' : 'server'] = modId
}
})

for (let id in pluginState.serverActions) {
const action = pluginState.serverActions[id]
for (let name in action.workers) {
const modId =
pluginState.actionModServerId[name][
action.layer[name] === WEBPACK_LAYERS.actionBrowser
? 'client'
: 'server'
]
action.workers[name] = modId!
}
serverActions[id] = action
for (let id in pluginState.serverActions) {
const action = pluginState.serverActions[id]
for (let name in action.workers) {
const modId =
pluginState.actionModServerId[name][
action.layer[name] === WEBPACK_LAYERS.actionBrowser
? 'client'
: 'server'
]
action.workers[name] = modId!
}
serverActions[id] = action
}

for (let id in pluginState.edgeServerActions) {
const action = pluginState.edgeServerActions[id]
for (let name in action.workers) {
const modId =
pluginState.actionModEdgeServerId[name][
action.layer[name] === WEBPACK_LAYERS.actionBrowser
? 'client'
: 'server'
]
action.workers[name] = modId!
}
edgeServerActions[id] = action
for (let id in pluginState.edgeServerActions) {
const action = pluginState.edgeServerActions[id]
for (let name in action.workers) {
const modId =
pluginState.actionModEdgeServerId[name][
action.layer[name] === WEBPACK_LAYERS.actionBrowser
? 'client'
: 'server'
]
action.workers[name] = modId!
}
edgeServerActions[id] = action
}

const json = JSON.stringify(
Expand Down