fix(nextjs): fixes for convert-to-inferred (#26735)

…nferred

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #

---------

Co-authored-by: Nicholas Cunningham <ndcunningham@gmail.com>
This commit is contained in:
Jack Hsu 2024-06-27 13:58:16 -04:00 committed by GitHub
parent d90a735540
commit c75e7ef683
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 147 additions and 11 deletions

View File

@ -381,8 +381,10 @@ describe('convert-to-inferred', () => {
{},
{
build: {
debug: true,
profile: true,
experimentalAppOnly: true,
experimentalBuildMode: true,
experimentalBuildMode: 'generate',
},
}
);
@ -391,10 +393,33 @@ describe('convert-to-inferred', () => {
await convertToInferred(tree, { project: project.name });
const projectConfig = readProjectConfiguration(tree, project.name);
expect(projectConfig.targets.build).toMatchObject({
options: {
args: ['--experimental-app-only', '--experimental-build-mode'],
},
expect(projectConfig.targets.build.options).toEqual({
args: [
'--debug',
'--profile',
'--experimental-app-only',
'--experimental-build-mode generate',
],
});
});
it('should not migrate options to CLI args if they are booleans and are false', async () => {
const project = createProject(
tree,
{},
{
build: {
debug: false,
profile: false,
experimentalAppOnly: false,
},
}
);
writeNextConfig(tree, project.root);
await convertToInferred(tree, { project: project.name });
const projectConfig = readProjectConfiguration(tree, project.name);
expect(projectConfig.targets.build.options).toBeUndefined();
});
});

View File

@ -157,17 +157,36 @@ function handlePropertiesFromTargetOptions(
delete options.watch;
}
if ('experimentalAppOnly' in options && options.experimentalAppOnly) {
options['args'] ??= [];
options['args'].push('--experimental-app-only');
if ('debug' in options) {
if (options.debug) {
options['args'] ??= [];
options['args'].push('--debug');
}
delete options.debug;
}
if ('profile' in options) {
if (options.profile) {
options['args'] ??= [];
options['args'].push('--profile');
}
delete options.profile;
}
if ('experimentalAppOnly' in options) {
if (options.experimentalAppOnly) {
options['args'] ??= [];
options['args'].push('--experimental-app-only');
}
delete options.experimentalAppOnly;
}
if ('experimentalBuildMode' in options && options.experimentalBuildMode) {
if ('experimentalBuildMode' in options) {
options['args'] ??= [];
options['args'].push(`--experimental-build-mode`);
options['args'].push(
`--experimental-build-mode ${options.experimentalBuildMode}`
);
delete options.experimentalBuildMode;
}
configValues[configuration] = configMap;
}

View File

@ -145,4 +145,80 @@ describe('UpdateNextConfig', () => {
project: 'no-compose-plugins',
});
});
describe('Reserved variables', () => {
it('should warn the user if the next config file contains reserved variables', () => {
const initConfig = `
const options = {};
const configValues = {};
const configuration = {};
`;
const projectDetails = {
projectName: 'reserved-variables',
root: 'reserved-variables',
};
tree.write(`${projectDetails.root}/next.config.js`, initConfig);
updateNextConfig(tree, '', projectDetails, mockLog);
expect(mockLog.addLog).toHaveBeenCalledWith({
executorName: '@nx/next:build',
log: "The project (reserved-variables) Next.js config contains reserved variables ('options', 'configValues' or 'configuration') which are generated during the migration. Leaving it as is.",
project: 'reserved-variables',
});
});
it('should warn the user if the next config file contains a reserved variable (option)', () => {
const initConfig = `const options = {};`;
const projectDetails = {
projectName: 'reserved-options',
root: 'reserved-options',
};
tree.write(`${projectDetails.root}/next.config.js`, initConfig);
updateNextConfig(tree, '', projectDetails, mockLog);
expect(mockLog.addLog).toHaveBeenCalledWith({
executorName: '@nx/next:build',
log: "The project (reserved-options) Next.js config contains reserved variables ('options', 'configValues' or 'configuration') which are generated during the migration. Leaving it as is.",
project: 'reserved-options',
});
});
it('should warn the user if the next config file contains a reserved variable (configValues)', () => {
const initConfig = `const configValues = {};`;
const projectDetails = {
projectName: 'reserved-config-values',
root: 'reserved-config-values',
};
tree.write(`${projectDetails.root}/next.config.js`, initConfig);
updateNextConfig(tree, '', projectDetails, mockLog);
expect(mockLog.addLog).toHaveBeenCalledWith({
executorName: '@nx/next:build',
log: "The project (reserved-config-values) Next.js config contains reserved variables ('options', 'configValues' or 'configuration') which are generated during the migration. Leaving it as is.",
project: 'reserved-config-values',
});
});
it('should warn the user if the next config file contains a reserved variable (configuration)', () => {
const initConfig = `const configuration = {};`;
const projectDetails = {
projectName: 'reserved-configuration-values',
root: 'reserved-configuration-values',
};
tree.write(`${projectDetails.root}/next.config.js`, initConfig);
updateNextConfig(tree, '', projectDetails, mockLog);
expect(mockLog.addLog).toHaveBeenCalledWith({
executorName: '@nx/next:build',
log: "The project (reserved-configuration-values) Next.js config contains reserved variables ('options', 'configValues' or 'configuration') which are generated during the migration. Leaving it as is.",
project: 'reserved-configuration-values',
});
});
});
});

View File

@ -23,6 +23,22 @@ export function updateNextConfig(
const nextConfigContents = tree.read(nextConfigPath, 'utf-8');
let ast = tsquery.ast(nextConfigContents);
const reservedVarQuery = `
VariableStatement > VariableDeclarationList > VariableDeclaration:has(Identifier[name=configValues]),
VariableStatement > VariableDeclarationList > VariableDeclaration:has(Identifier[name=configuration]),
VariableStatement > VariableDeclarationList > VariableDeclaration:has(Identifier[name=options])
`;
const matches = tsquery(ast, reservedVarQuery);
if (matches.length > 0) {
migrationLogs.addLog({
project: project.projectName,
executorName: '@nx/next:build',
log: `The project (${project.projectName}) Next.js config contains reserved variables ('options', 'configValues' or 'configuration') which are generated during the migration. Leaving it as is.`,
});
return;
}
// Query to check for composePlugins in module.exports
const composePluginsQuery = `ExpressionStatement > BinaryExpression > CallExpression > CallExpression:has(Identifier[name=composePlugins])`;
const composePluginNode = tsquery(ast, composePluginsQuery)[0];