Skip to content

Commit

Permalink
rework next/link flag for Next.js 12 and 13
Browse files Browse the repository at this point in the history
  • Loading branch information
yannbf committed Nov 18, 2022
1 parent cc62687 commit 4173604
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 77 deletions.
78 changes: 17 additions & 61 deletions code/frameworks/nextjs/src/config/webpack.ts
@@ -1,11 +1,9 @@
import type { Configuration as WebpackConfig } from 'webpack';
import { PHASE_DEVELOPMENT_SERVER } from 'next/constants';
import findUp from 'find-up';
import { pathExists } from 'fs-extra';
import semver from 'semver';

import type { NextConfig } from 'next';
import dedent from 'ts-dedent';
import { DefinePlugin } from 'webpack';
import { addScopedAlias } from '../utils';
import { addScopedAlias, getNextjsVersion, resolveNextConfig } from '../utils';

export const configureConfig = async ({
baseConfig,
Expand All @@ -24,63 +22,21 @@ export const configureConfig = async ({
return nextConfig;
};

const findNextConfigFile = async (configDir: string) => {
const supportedExtensions = ['mjs', 'js'];
return supportedExtensions.reduce<Promise<undefined | string>>(
async (acc, ext: string | undefined) => {
const resolved = await acc;
if (!resolved) {
acc = findUp(`next.config.${ext}`, { cwd: configDir });
}

return acc;
},
Promise.resolve(undefined)
);
};

const resolveNextConfig = async ({
baseConfig,
nextConfigPath,
configDir,
}: {
baseConfig: WebpackConfig;
nextConfigPath?: string;
configDir: string;
}): Promise<NextConfig> => {
const nextConfigFile = nextConfigPath || (await findNextConfigFile(configDir));

if (!nextConfigFile || (await pathExists(nextConfigFile)) === false) {
throw new Error(
dedent`
Could not find or resolve your Next config file. Please provide the next config file path as a framework option.
const version = getNextjsVersion();

More info: https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/README.md#options
`
);
const setupRuntimeConfig = (baseConfig: WebpackConfig, nextConfig: NextConfig): void => {
const definePluginConfig: Record<string, any> = {
// this mimics what nextjs does client side
// https://github.com/vercel/next.js/blob/57702cb2a9a9dba4b552e0007c16449cf36cfb44/packages/next/client/index.tsx#L101
'process.env.__NEXT_RUNTIME_CONFIG': JSON.stringify({
serverRuntimeConfig: {},
publicRuntimeConfig: nextConfig.publicRuntimeConfig,
}),
};

if (semver.gte(version, '13.0.0') || nextConfig.experimental?.newNextLinkBehavior) {
definePluginConfig['process.env.__NEXT_NEW_LINK_BEHAVIOR'] = true;
}

const nextConfigExport = await import(nextConfigFile);

const nextConfig =
typeof nextConfigExport === 'function'
? nextConfigExport(PHASE_DEVELOPMENT_SERVER, {
defaultConfig: baseConfig,
})
: nextConfigExport;

return nextConfig;
};

const setupRuntimeConfig = (baseConfig: WebpackConfig, nextConfig: NextConfig): void => {
baseConfig.plugins?.push(
new DefinePlugin({
// this mimics what nextjs does client side
// https://github.com/vercel/next.js/blob/57702cb2a9a9dba4b552e0007c16449cf36cfb44/packages/next/client/index.tsx#L101
'process.env.__NEXT_RUNTIME_CONFIG': JSON.stringify({
serverRuntimeConfig: {},
publicRuntimeConfig: nextConfig.publicRuntimeConfig,
}),
})
);
baseConfig.plugins?.push(new DefinePlugin(definePluginConfig));
};
16 changes: 1 addition & 15 deletions code/frameworks/nextjs/src/preset.ts
@@ -1,6 +1,5 @@
// https://storybook.js.org/docs/react/addons/writing-presets
import { dirname, join } from 'path';
import semver from 'semver';
import type { Options, PresetProperty } from '@storybook/types';
import type { TransformOptions } from '@babel/core';
import { configureConfig } from './config/webpack';
Expand All @@ -10,7 +9,7 @@ import { configureRouting } from './routing/webpack';
import { configureStyledJsx } from './styledJsx/webpack';
import { configureStyledJsxTransforms } from './styledJsx/babel';
import { configureImages } from './images/webpack';
import { configureRuntimeNextjsVersionResolution, getNextjsVersion } from './utils';
import { configureRuntimeNextjsVersionResolution } from './utils';
import type { FrameworkOptions, StorybookConfig } from './types';
import { configureTypescript } from './config/babel';

Expand All @@ -19,8 +18,6 @@ export const addons: PresetProperty<'addons', StorybookConfig> = [
dirname(require.resolve(join('@storybook/builder-webpack5', 'package.json'))),
];

const version = getNextjsVersion();

const defaultFrameworkOptions: FrameworkOptions = {};

export const frameworkOptions = async (
Expand Down Expand Up @@ -71,17 +68,6 @@ export const config: StorybookConfig['previewAnnotations'] = (entry = []) => [
require.resolve('@storybook/nextjs/preview.js'),
];

export const env = (envConfig: PresetProperty<'env', StorybookConfig>) => {
return {
...envConfig,
// Can be removed if https://github.com/vercel/next.js/issues/42621 is resolved
...(semver.gte(version, '13.0.0') && {
// TODO: This should also respect `newNextLinkBehavior`: https://github.com/vercel/next.js/blob/07d3da102dfef65be9c13fd4b754a12eda7eded1/packages/next/server/config-shared.ts#L88
__NEXT_NEW_LINK_BEHAVIOR: 'true',
}),
};
};

// Not even sb init - automigrate - running dev
// You're using a version of Nextjs prior to v10, which is unsupported by this framework.

Expand Down
55 changes: 54 additions & 1 deletion code/frameworks/nextjs/src/utils.ts
@@ -1,6 +1,11 @@
import path from 'path';
import type { Configuration as WebpackConfig } from 'webpack';
import { DefinePlugin } from 'webpack';
import { PHASE_DEVELOPMENT_SERVER } from 'next/constants';
import findUp from 'find-up';
import { pathExists } from 'fs-extra';
import dedent from 'ts-dedent';
import type { Configuration as WebpackConfig } from 'webpack';
import type { NextConfig } from 'next';

export const configureRuntimeNextjsVersionResolution = (baseConfig: WebpackConfig): void => {
baseConfig.plugins?.push(
Expand All @@ -12,6 +17,54 @@ export const configureRuntimeNextjsVersionResolution = (baseConfig: WebpackConfi

export const getNextjsVersion = (): string => require(scopedResolve('next/package.json')).version;

const findNextConfigFile = async (configDir: string) => {
const supportedExtensions = ['mjs', 'js'];
return supportedExtensions.reduce<Promise<undefined | string>>(
async (acc, ext: string | undefined) => {
const resolved = await acc;
if (!resolved) {
acc = findUp(`next.config.${ext}`, { cwd: configDir });
}

return acc;
},
Promise.resolve(undefined)
);
};

export const resolveNextConfig = async ({
baseConfig = {},
nextConfigPath,
configDir,
}: {
baseConfig?: WebpackConfig;
nextConfigPath?: string;
configDir: string;
}): Promise<NextConfig> => {
const nextConfigFile = nextConfigPath || (await findNextConfigFile(configDir));

if (!nextConfigFile || (await pathExists(nextConfigFile)) === false) {
throw new Error(
dedent`
Could not find or resolve your Next config file. Please provide the next config file path as a framework option.
More info: https://github.com/storybookjs/storybook/blob/next/code/frameworks/nextjs/README.md#options
`
);
}

const nextConfigExport = await import(nextConfigFile);

const nextConfig =
typeof nextConfigExport === 'function'
? nextConfigExport(PHASE_DEVELOPMENT_SERVER, {
defaultConfig: baseConfig,
})
: nextConfigExport;

return nextConfig.default || nextConfig;
};

// This is to help the addon in development
// Without it, webpack resolves packages in its node_modules instead of the example's node_modules
export const addScopedAlias = (baseConfig: WebpackConfig, name: string, alias?: string): void => {
Expand Down

0 comments on commit 4173604

Please sign in to comment.