feat(nextjs): Add support for create nodes for nextjs (#20193)

This commit is contained in:
Nicholas Cunningham 2023-12-06 16:52:09 -07:00 committed by GitHub
parent 7ffc3284f6
commit b8d24e6d0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 619 additions and 84 deletions

View File

@ -0,0 +1,63 @@
import {
runCLI,
cleanupProject,
newProject,
uniq,
updateJson,
runE2ETests,
directoryExists,
readJson,
} from 'e2e/utils';
describe('@nx/next/plugin', () => {
let project: string;
let appName: string;
beforeAll(() => {
project = newProject();
appName = uniq('app');
runCLI(
`generate @nx/next:app ${appName} --project-name-and-root-format=as-provided --no-interactive`,
{ env: { NX_PCV3: 'true' } }
);
// update package.json to add next as a script
updateJson(`package.json`, (json) => {
json.scripts = json.scripts || {};
json.scripts.next = 'next';
return json;
});
});
afterAll(() => cleanupProject());
it('nx.json should contain plugin configuration', () => {
const nxJson = readJson('nx.json');
const nextPlugin = nxJson.plugins.find(
(plugin) => plugin.plugin === '@nx/next/plugin'
);
expect(nextPlugin).toBeDefined();
expect(nextPlugin.options).toBeDefined();
expect(nextPlugin.options.buildTargetName).toEqual('build');
expect(nextPlugin.options.startTargetName).toEqual('start');
expect(nextPlugin.options.devTargetName).toEqual('dev');
});
it('should build the app', async () => {
const result = runCLI(`build ${appName}`);
// check build output for PCV3 artifacts (e.g. .next directory) are inside the project directory
directoryExists(`${appName}/.next`);
expect(result).toContain(
`Successfully ran target build for project ${appName}`
);
}, 200_000);
it('should serve the app', async () => {
if (runE2ETests()) {
const e2eResult = runCLI(`run ${appName}-e2e:e2e --verbose`);
expect(e2eResult).toContain('All specs passed!');
}
}, 500_000);
});

View File

@ -94,6 +94,6 @@ describe('Next.js Webpack', () => {
expect(() => {
runCLI(`build ${appName}`);
}).not.toThrow();
checkFilesExist(`apps/${appName}/.next/build-manifest.json`);
checkFilesExist(`dist/apps/${appName}/.next/build-manifest.json`);
}, 300_000);
});

View File

@ -57,6 +57,37 @@ describe('Next.js Experimental Features', () => {
`
);
updateFile(
`apps/${appName}/next.config.js`,
`
//@ts-check
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { composePlugins, withNx } = require('@nx/next');
/**
* @type {import('@nx/next/plugins/with-nx').WithNxOptions}
**/
const nextConfig = {
nx: {
// Set this to true if you would like to use SVGR
// See: https://github.com/gregberge/svgr
svgr: false,
},
experimental: {
serverActions: true
}
};
const plugins = [
// Add more Next.js plugins to this list if needed.
withNx,
];
module.exports = composePlugins(...plugins)(nextConfig);
`
);
await checkApp(appName, {
checkUnitTest: false,
checkLint: true,

View File

@ -147,7 +147,7 @@ function waitForServer(
let pollTimeout: NodeJS.Timeout | null;
const { protocol } = new URL(url);
const timeoutDuration = webServerConfig?.timeout ?? 5 * 1000;
const timeoutDuration = webServerConfig?.timeout ?? 10 * 1000;
const timeout = setTimeout(() => {
clearTimeout(pollTimeout);
reject(

View File

@ -34,6 +34,7 @@
"next": ">=13.0.0"
},
"dependencies": {
"@nx/devkit": "file:../devkit",
"@babel/plugin-proposal-decorators": "^7.22.7",
"@svgr/webpack": "^8.0.1",
"chalk": "^4.1.0",
@ -44,7 +45,6 @@
"url-loader": "^4.1.1",
"tslib": "^2.3.0",
"webpack-merge": "^5.8.0",
"@nx/devkit": "file:../devkit",
"@nx/js": "file:../js",
"@nx/eslint": "file:../eslint",
"@nx/react": "file:../react",

1
packages/next/plugin.ts Normal file
View File

@ -0,0 +1 @@
export { createNodes, NextPluginOptions } from './src/plugins/plugin';

View File

@ -5,12 +5,11 @@
import type { NextConfig } from 'next';
import type { NextConfigFn } from '../src/utils/config';
import type { NextBuildBuilderOptions } from '../src/utils/types';
import type { DependentBuildableProjectNode } from '@nx/js/src/utils/buildable-libs-utils';
import type {
ExecutorContext,
ProjectGraph,
ProjectGraphProjectNode,
Target,
import {
type ExecutorContext,
type ProjectGraph,
type ProjectGraphProjectNode,
type Target,
} from '@nx/devkit';
const baseNXEnvironmentVariables = [
@ -48,6 +47,7 @@ const baseNXEnvironmentVariables = [
'NX_MAPPINGS',
'NX_FILE_TO_RUN',
'NX_NEXT_PUBLIC_DIR',
'NX_CYPRESS_COMPONENT_TEST',
];
export interface WithNxOptions extends NextConfig {
@ -150,7 +150,10 @@ function withNx(
const { PHASE_PRODUCTION_SERVER, PHASE_DEVELOPMENT_SERVER } = await import(
'next/constants'
);
if (PHASE_PRODUCTION_SERVER === phase) {
if (
PHASE_PRODUCTION_SERVER === phase ||
!process.env.NX_TASK_TARGET_TARGET
) {
// If we are running an already built production server, just return the configuration.
// NOTE: Avoid any `require(...)` or `import(...)` statements here. Development dependencies are not available at production runtime.
const { nx, ...validNextConfig } = _nextConfig;
@ -161,15 +164,22 @@ function withNx(
} else {
const {
createProjectGraphAsync,
readCachedProjectGraph,
joinPathFragments,
offsetFromRoot,
workspaceRoot,
} = require('@nx/devkit');
// Otherwise, add in webpack and eslint configuration for build or test.
let dependencies: DependentBuildableProjectNode[] = [];
const graph = await createProjectGraphAsync();
let graph = readCachedProjectGraph();
if (!graph) {
try {
graph = await createProjectGraphAsync();
} catch (e) {
throw new Error(
'Could not create project graph. Please ensure that your workspace is valid.'
);
}
}
const originalTarget = {
project: process.env.NX_TASK_TARGET_PROJECT,
@ -181,25 +191,9 @@ function withNx(
node: projectNode,
options,
projectName: project,
targetName,
configurationName,
} = getNxContext(graph, originalTarget);
const projectDirectory = projectNode.data.root;
if (options.buildLibsFromSource === false && targetName) {
const {
calculateProjectDependencies,
} = require('@nx/js/src/utils/buildable-libs-utils');
const result = calculateProjectDependencies(
graph,
workspaceRoot,
project,
targetName,
configurationName
);
dependencies = result.dependencies;
}
// Get next config
const nextConfig = getNextConfig(_nextConfig, context);
@ -229,18 +223,16 @@ function withNx(
// outputPath may be undefined if using run-commands or other executors other than @nx/next:build.
// In this case, the user should set distDir in their next.config.js.
if (options.outputPath) {
if (options.outputPath && phase !== PHASE_DEVELOPMENT_SERVER) {
const outputDir = `${offsetFromRoot(projectDirectory)}${
options.outputPath
}`;
// If running dev-server, we should keep `.next` inside project directory since Turbopack expects this.
// See: https://github.com/nrwl/nx/issues/19365
if (phase !== PHASE_DEVELOPMENT_SERVER) {
nextConfig.distDir =
nextConfig.distDir && nextConfig.distDir !== '.next'
? joinPathFragments(outputDir, nextConfig.distDir)
: joinPathFragments(outputDir, '.next');
}
nextConfig.distDir =
nextConfig.distDir && nextConfig.distDir !== '.next'
? joinPathFragments(outputDir, nextConfig.distDir)
: joinPathFragments(outputDir, '.next');
}
const userWebpackConfig = nextConfig.webpack;

View File

@ -2,6 +2,7 @@ import {
ExecutorContext,
parseTargetString,
readTargetOptions,
targetToTargetString,
workspaceLayout,
} from '@nx/devkit';
import exportApp from 'next/dist/export';
@ -53,10 +54,12 @@ export default async function exportExecutor(
dependencies = result.dependencies;
}
// Returns { project: ProjectGraphNode; target: string; configuration?: string;}
const buildTarget = parseTargetString(options.buildTarget, context);
try {
const args = getBuildTargetCommand(options);
const buildTargetName = targetToTargetString(buildTarget);
const args = getBuildTargetCommand(buildTargetName);
execFileSync(pmCmd, args, {
stdio: [0, 1, 2],
});
@ -88,7 +91,7 @@ export default async function exportExecutor(
return { success: true };
}
function getBuildTargetCommand(options: NextExportBuilderOptions) {
const cmd = ['nx', 'run', options.buildTarget];
function getBuildTargetCommand(buildTarget: string) {
const cmd = ['nx', 'run', buildTarget];
return cmd;
}

View File

@ -24,7 +24,7 @@ export default async function* serveExecutor(
}
const buildOptions = readTargetOptions<NextBuildBuilderOptions>(
parseTargetString(options.buildTarget, context.projectGraph),
parseTargetString(options.buildTarget, context),
context
);
const projectRoot = context.workspace.projects[context.projectName].root;

View File

@ -6,6 +6,7 @@ import {
Tree,
} from '@nx/devkit';
import { Schema } from './schema';
import { applicationGenerator } from './application';
describe('app', () => {
@ -731,6 +732,48 @@ describe('app', () => {
});
});
describe('app with Project Configuration V3 enabeled', () => {
let tree: Tree;
let originalPVC3;
const schema: Schema = {
name: 'app',
appDir: true,
unitTestRunner: 'jest',
style: 'css',
e2eTestRunner: 'cypress',
projectNameAndRootFormat: 'as-provided',
};
beforeAll(() => {
tree = createTreeWithEmptyWorkspace();
originalPVC3 = process.env['NX_PCV3'];
process.env['NX_PCV3'] = 'true';
});
afterAll(() => {
if (originalPVC3) {
process.env['NX_PCV3'] = originalPVC3;
} else {
delete process.env['NX_PCV3'];
}
});
it('should not generate build serve and export targets', async () => {
const name = uniq();
await applicationGenerator(tree, {
...schema,
name,
});
const projectConfiguration = readProjectConfiguration(tree, name);
expect(projectConfiguration.targets.build).toBeUndefined();
expect(projectConfiguration.targets.serve).toBeUndefined();
expect(projectConfiguration.targets.export).toBeUndefined();
});
});
function uniq() {
return `str-${(Math.random() * 10000).toFixed(0)}`;
}

View File

@ -20,6 +20,7 @@ import { addLinting } from './lib/add-linting';
import { customServerGenerator } from '../custom-server/custom-server';
import { updateCypressTsConfig } from './lib/update-cypress-tsconfig';
import { showPossibleWarnings } from './lib/show-possible-warnings';
import { addPlugin } from './lib/add-plugin';
export async function applicationGenerator(host: Tree, schema: Schema) {
return await applicationGeneratorInternal(host, {
@ -41,6 +42,7 @@ export async function applicationGeneratorInternal(host: Tree, schema: Schema) {
tasks.push(nextTask);
createApplicationFiles(host, options);
addProject(host, options);
const e2eTask = await addE2e(host, options);

View File

@ -3,6 +3,7 @@ import {
ensurePackage,
getPackageManagerCommand,
joinPathFragments,
readNxJson,
Tree,
} from '@nx/devkit';
import { Linter } from '@nx/eslint';
@ -11,6 +12,12 @@ import { nxVersion } from '../../../utils/versions';
import { NormalizedSchema } from './normalize-options';
export async function addE2e(host: Tree, options: NormalizedSchema) {
const nxJson = readNxJson(host);
const hasPlugin = nxJson.plugins?.some((p) =>
typeof p === 'string'
? p === '@nx/next/plugin'
: p.plugin === '@nx/next/plugin'
);
if (options.e2eTestRunner === 'cypress') {
const { configurationGenerator } = ensurePackage<
typeof import('@nx/cypress')
@ -28,8 +35,10 @@ export async function addE2e(host: Tree, options: NormalizedSchema) {
project: options.e2eProjectName,
directory: 'src',
skipFormat: true,
devServerTarget: `${options.projectName}:serve`,
baseUrl: 'http://localhost:4200',
devServerTarget: `${options.projectName}:${
hasPlugin ? 'start' : 'serve'
}`,
baseUrl: `http://localhost:${hasPlugin ? '3000' : '4200'}`,
jsx: true,
});
} else if (options.e2eTestRunner === 'playwright') {
@ -50,10 +59,10 @@ export async function addE2e(host: Tree, options: NormalizedSchema) {
js: false,
linter: options.linter,
setParserOptionsProject: options.setParserOptionsProject,
webServerAddress: 'http://127.0.0.1:4200',
webServerCommand: `${getPackageManagerCommand().exec} nx serve ${
options.projectName
}`,
webServerAddress: `http://127.0.0.1:${hasPlugin ? '3000' : '4200'}`,
webServerCommand: `${getPackageManagerCommand().exec} nx ${
hasPlugin ? 'start' : 'serve'
} ${options.projectName}`,
});
}
return () => {};

View File

@ -0,0 +1,27 @@
import { Tree, readNxJson, updateNxJson } from '@nx/devkit';
export function addPlugin(tree: Tree) {
const nxJson = readNxJson(tree);
nxJson.plugins ??= [];
for (const plugin of nxJson.plugins) {
if (
typeof plugin === 'string'
? plugin === '@nx/next/plugin'
: plugin.plugin === '@nx/next/plugin'
) {
return;
}
}
nxJson.plugins.push({
plugin: '@nx/next/plugin',
options: {
buildTargetName: 'build',
serveTargetName: 'serve',
exportTargetName: 'export',
},
});
updateNxJson(tree, nxJson);
}

View File

@ -2,52 +2,65 @@ import { NormalizedSchema } from './normalize-options';
import {
addProjectConfiguration,
ProjectConfiguration,
readNxJson,
Tree,
} from '@nx/devkit';
export function addProject(host: Tree, options: NormalizedSchema) {
const targets: Record<string, any> = {};
targets.build = {
executor: '@nx/next:build',
outputs: ['{options.outputPath}'],
defaultConfiguration: 'production',
options: {
outputPath: options.outputPath,
},
configurations: {
development: {
outputPath: options.appProjectRoot,
},
production: {},
},
};
// Check if plugin exists in nx.json and if it doesn't then we can continue
// with the default targets.
targets.serve = {
executor: '@nx/next:server',
defaultConfiguration: 'development',
options: {
buildTarget: `${options.projectName}:build`,
dev: true,
},
configurations: {
development: {
buildTarget: `${options.projectName}:build:development`,
const nxJson = readNxJson(host);
const hasPlugin = nxJson.plugins?.some((p) =>
typeof p === 'string'
? p === '@nx/next/plugin'
: p.plugin === '@nx/next/plugin'
);
if (!hasPlugin) {
targets.build = {
executor: '@nx/next:build',
outputs: ['{options.outputPath}'],
defaultConfiguration: 'production',
options: {
outputPath: options.outputPath,
},
configurations: {
development: {
outputPath: options.appProjectRoot,
},
production: {},
},
};
targets.serve = {
executor: '@nx/next:server',
defaultConfiguration: 'development',
options: {
buildTarget: `${options.projectName}:build`,
dev: true,
},
production: {
buildTarget: `${options.projectName}:build:production`,
dev: false,
configurations: {
development: {
buildTarget: `${options.projectName}:build:development`,
dev: true,
},
production: {
buildTarget: `${options.projectName}:build:production`,
dev: false,
},
},
},
};
};
targets.export = {
executor: '@nx/next:export',
options: {
buildTarget: `${options.projectName}:build:production`,
},
};
targets.export = {
executor: '@nx/next:export',
options: {
buildTarget: `${options.projectName}:build:production`,
},
};
}
const project: ProjectConfiguration = {
root: options.appProjectRoot,

View File

@ -2,6 +2,7 @@ import {
addDependenciesToPackageJson,
ensurePackage,
GeneratorCallback,
readNxJson,
runTasksInSerial,
Tree,
} from '@nx/devkit';
@ -18,6 +19,7 @@ import {
} from '../../utils/versions';
import { InitSchema } from './schema';
import { addGitIgnoreEntry } from '../../utils/add-gitignore-entry';
import { addPlugin } from './lib/add-plugin';
function updateDependencies(host: Tree) {
return addDependenciesToPackageJson(
@ -85,6 +87,9 @@ export async function nextInitGenerator(host: Tree, schema: InitSchema) {
}
addGitIgnoreEntry(host);
if (process.env.NX_PCV3 === 'true') {
addPlugin(host);
}
return runTasksInSerial(...tasks);
}

View File

@ -0,0 +1,27 @@
import { Tree, readNxJson, updateNxJson } from '@nx/devkit';
export function addPlugin(tree: Tree) {
const nxJson = readNxJson(tree);
nxJson.plugins ??= [];
for (const plugin of nxJson.plugins) {
if (
typeof plugin === 'string'
? plugin === '@nx/next/plugin'
: plugin.plugin === '@nx/next/plugin'
) {
return;
}
}
nxJson.plugins.push({
plugin: '@nx/next/plugin',
options: {
buildTargetName: 'build',
devTargetName: 'dev',
startTargetName: 'start',
},
});
updateNxJson(tree, nxJson);
}

View File

@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`@nx/next/plugin integrated projects should create nodes 1`] = `Promise {}`;
exports[`@nx/next/plugin root projects should create nodes 1`] = `Promise {}`;

View File

@ -0,0 +1,95 @@
import { CreateNodesContext } from '@nx/devkit';
import type { NextConfig } from 'next';
import { createNodes } from './plugin';
import { TempFs } from '@nx/devkit/internal-testing-utils';
describe('@nx/next/plugin', () => {
let createNodesFunction = createNodes[1];
let context: CreateNodesContext;
describe('root projects', () => {
beforeEach(async () => {
context = {
nxJsonConfiguration: {
namedInputs: {
default: ['{projectRoot}/**/*'],
production: ['!{projectRoot}/**/*.spec.ts'],
},
},
workspaceRoot: '',
};
});
afterEach(() => {
jest.resetModules();
});
it('should create nodes', () => {
const nextConfigPath = 'next.config.js';
mockNextConfig(nextConfigPath, {});
const nodes = createNodesFunction(
nextConfigPath,
{
buildTargetName: 'build',
devTargetName: 'dev',
startTargetName: 'start',
},
context
);
expect(nodes).toMatchSnapshot();
});
});
describe('integrated projects', () => {
const tempFs = new TempFs('test');
beforeEach(() => {
context = {
nxJsonConfiguration: {
namedInputs: {
default: ['{projectRoot}/**/*'],
production: ['!{projectRoot}/**/*.spec.ts'],
},
},
workspaceRoot: tempFs.tempDir,
};
tempFs.createFileSync(
'my-app/project.json',
JSON.stringify({ name: 'my-app' })
);
tempFs.createFileSync('my-app/next.config.js', '');
});
afterEach(() => {
jest.resetModules();
});
it('should create nodes', () => {
mockNextConfig('my-app/next.config.js', {});
const nodes = createNodesFunction(
'my-app/next.config.js',
{
buildTargetName: 'my-build',
devTargetName: 'my-serve',
startTargetName: 'my-start',
},
context
);
expect(nodes).toMatchSnapshot();
});
});
});
function mockNextConfig(path: string, config: NextConfig) {
jest.mock(
path,
() => ({
default: config,
}),
{
virtual: true,
}
);
}

View File

@ -0,0 +1,219 @@
import {
CreateDependencies,
CreateNodes,
CreateNodesContext,
NxJsonConfiguration,
TargetConfiguration,
detectPackageManager,
readJsonFile,
writeJsonFile,
} from '@nx/devkit';
import { dirname, join } from 'path';
import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs';
import { existsSync, readdirSync } from 'fs';
import { projectGraphCacheDirectory } from 'nx/src/utils/cache-directory';
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
import { type NextConfig } from 'next';
import { PHASE_PRODUCTION_BUILD } from 'next/constants';
import { getLockFileName } from '@nx/js';
export interface NextPluginOptions {
buildTargetName?: string;
devTargetName?: string;
startTargetName?: string;
}
const cachePath = join(projectGraphCacheDirectory, 'next.hash');
const targetsCache = existsSync(cachePath) ? readTargetsCache() : {};
const calculatedTargets: Record<
string,
Record<string, TargetConfiguration>
> = {};
function readTargetsCache(): Record<
string,
Record<string, TargetConfiguration>
> {
return readJsonFile(cachePath);
}
function writeTargetsToCache(
targets: Record<string, Record<string, TargetConfiguration>>
) {
writeJsonFile(cachePath, targets);
}
export const createDependencies: CreateDependencies = () => {
writeTargetsToCache(calculatedTargets);
return [];
};
// TODO(nicholas): Add support for .mjs files
export const createNodes: CreateNodes<NextPluginOptions> = [
'**/next.config.{js, cjs}',
async (configFilePath, options, context) => {
const projectRoot = dirname(configFilePath);
// Do not create a project if package.json and project.json isn't there.
const siblingFiles = readdirSync(join(context.workspaceRoot, projectRoot));
if (
!siblingFiles.includes('package.json') &&
!siblingFiles.includes('project.json')
) {
return {};
}
options = normalizeOptions(options);
const hash = calculateHashForCreateNodes(projectRoot, options, context, [
getLockFileName(detectPackageManager(context.workspaceRoot)),
]);
const targets =
targetsCache[hash] ??
(await buildNextTargets(configFilePath, projectRoot, options, context));
calculatedTargets[hash] = targets;
return {
projects: {
[projectRoot]: {
root: projectRoot,
targets,
},
},
};
},
];
async function buildNextTargets(
nextConfigPath: string,
projectRoot: string,
options: NextPluginOptions,
context: CreateNodesContext
) {
const nextConfig = getNextConfig(nextConfigPath, context);
const namedInputs = getNamedInputs(projectRoot, context);
const targets: Record<string, TargetConfiguration> = {};
targets[options.buildTargetName] = await getBuildTargetConfig(
namedInputs,
projectRoot,
nextConfig
);
targets[options.devTargetName] = getDevTargetConfig(projectRoot);
targets[options.startTargetName] = getStartTargetConfig(options, projectRoot);
return targets;
}
async function getBuildTargetConfig(
namedInputs: { [inputName: string]: any[] },
projectRoot: string,
nextConfig: NextConfig
) {
// Set output path here so that `withNx` can pick it up.
const targetConfig: TargetConfiguration = {
command: `next build`,
options: {
cwd: projectRoot,
},
dependsOn: ['^build'],
cache: true,
inputs: getInputs(namedInputs),
outputs: [await getOutputs(projectRoot, nextConfig)],
};
return targetConfig;
}
function getDevTargetConfig(projectRoot: string) {
const targetConfig: TargetConfiguration = {
command: `next dev`,
options: {
cwd: projectRoot,
},
};
return targetConfig;
}
function getStartTargetConfig(options: NextPluginOptions, projectRoot: string) {
const targetConfig: TargetConfiguration = {
command: `next start`,
options: {
cwd: projectRoot,
},
dependsOn: [options.buildTargetName],
};
return targetConfig;
}
async function getOutputs(projectRoot, nextConfig) {
let dir = '.next';
if (typeof nextConfig === 'function') {
// Works for both async and sync functions.
const configResult = await Promise.resolve(
nextConfig(PHASE_PRODUCTION_BUILD, { defaultConfig: {} })
);
if (configResult?.distDir) {
dir = configResult?.distDir;
}
} else if (typeof nextConfig === 'object' && nextConfig?.distDir) {
// If nextConfig is an object, directly use its 'distDir' property.
dir = nextConfig.distDir;
}
return `{workspaceRoot}/${projectRoot}/${dir}`;
}
function getNextConfig(
configFilePath: string,
context: CreateNodesContext
): Promise<any> {
const resolvedPath = join(context.workspaceRoot, configFilePath);
const module = load(resolvedPath);
return module.default ?? module;
}
function normalizeOptions(options: NextPluginOptions): NextPluginOptions {
options ??= {};
options.buildTargetName ??= 'build';
options.devTargetName ??= 'dev';
options.startTargetName ??= 'start';
return options;
}
function getInputs(
namedInputs: NxJsonConfiguration['namedInputs']
): TargetConfiguration['inputs'] {
return [
...('production' in namedInputs
? ['default', '^production']
: ['default', '^default']),
{
externalDependencies: ['next'],
},
];
}
/**
* Load the module after ensuring that the require cache is cleared.
*/
function load(path: string): any {
// Clear cache if the path is in the cache
if (require.cache[path]) {
for (const k of Object.keys(require.cache)) {
delete require.cache[k];
}
}
// Then require
return require(path);
}

View File

@ -1,7 +1,7 @@
export const nxVersion = require('../../package.json').version;
export const nextVersion = '13.4.1';
export const eslintConfigNextVersion = '13.4.1';
export const nextVersion = '13.4.4';
export const eslintConfigNextVersion = '13.4.4';
export const sassVersion = '1.62.1';
export const lessLoader = '11.1.0';
export const emotionServerVersion = '11.11.0';