fix(bundling): convert to inferred should handle config file (#26619)

<!-- 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 #
This commit is contained in:
Colum Ferry 2024-06-21 16:32:04 +01:00 committed by GitHub
parent f66ac74b64
commit f8ffc05e8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 820 additions and 252 deletions

View File

@ -223,10 +223,6 @@ describe('Cypress - Convert Executors To Plugin', () => {
await convertToInferred(tree, { project: 'myapp-e2e', skipFormat: true });
// ASSERT
// project.json modifications
const updatedProject = readProjectConfiguration(tree, project.name);
const targetKeys = Object.keys(updatedProject.targets);
expect(targetKeys).not.toContain('test');
// nx.json modifications
const nxJsonPlugins = readNxJson(tree).plugins;
@ -361,10 +357,6 @@ describe('Cypress - Convert Executors To Plugin', () => {
await convertToInferred(tree, { skipFormat: true });
// ASSERT
// project.json modifications
const updatedProject = readProjectConfiguration(tree, project.name);
const targetKeys = Object.keys(updatedProject.targets);
expect(targetKeys).not.toContain('e2e');
// nx.json modifications
const nxJsonPlugins = readNxJson(tree).plugins;
@ -412,10 +404,6 @@ describe('Cypress - Convert Executors To Plugin', () => {
await convertToInferred(tree, { skipFormat: true });
// ASSERT
// project.json modifications
const updatedProject = readProjectConfiguration(tree, project.name);
const targetKeys = Object.keys(updatedProject.targets);
['test'].forEach((key) => expect(targetKeys).not.toContain(key));
// nx.json modifications
const nxJsonPlugins = readNxJson(tree).plugins;
@ -448,10 +436,6 @@ describe('Cypress - Convert Executors To Plugin', () => {
await convertToInferred(tree, { skipFormat: true });
// ASSERT
// project.json modifications
const updatedProject = readProjectConfiguration(tree, project.name);
const targetKeys = Object.keys(updatedProject.targets);
['test'].forEach((key) => expect(targetKeys).not.toContain(key));
// nx.json modifications
const nxJsonPlugins = readNxJson(tree).plugins;
@ -508,10 +492,6 @@ describe('Cypress - Convert Executors To Plugin', () => {
await convertToInferred(tree, { skipFormat: true });
// ASSERT
// project.json modifications
const updatedProject = readProjectConfiguration(tree, project.name);
const targetKeys = Object.keys(updatedProject.targets);
['test'].forEach((key) => expect(targetKeys).not.toContain(key));
// nx.json modifications
const nxJsonPlugins = readNxJson(tree).plugins;
@ -557,12 +537,13 @@ describe('Cypress - Convert Executors To Plugin', () => {
// project.json modifications
const updatedProject = readProjectConfiguration(tree, project.name);
expect(updatedProject.targets.e2e).toMatchInlineSnapshot(`
{
"options": {
"runner-ui": true,
},
}
`);
{
"options": {
"config-file": "./cypress.config.ts",
"runner-ui": true,
},
}
`);
// nx.json modifications
const nxJsonPlugins = readNxJson(tree).plugins;
@ -603,12 +584,13 @@ describe('Cypress - Convert Executors To Plugin', () => {
// project.json modifications
const updatedProject = readProjectConfiguration(tree, project.name);
expect(updatedProject.targets.e2e).toMatchInlineSnapshot(`
{
"options": {
"no-exit": true,
},
}
`);
{
"options": {
"config-file": "./cypress.config.ts",
"no-exit": true,
},
}
`);
// nx.json modifications
const nxJsonPlugins = readNxJson(tree).plugins;

View File

@ -10,6 +10,10 @@ import { targetOptionsToCliMap } from './lib/target-options-map';
import { upsertBaseUrl } from './lib/upsert-baseUrl';
import { addDevServerTargetToConfig } from './lib/add-dev-server-target-to-config';
import { addExcludeSpecPattern } from './lib/add-exclude-spec-pattern';
import {
processTargetOutputs,
toProjectRelativePath,
} from '@nx/devkit/src/generators/plugin-migrations/plugin-migration-utils';
interface Schema {
project?: string;
@ -61,83 +65,35 @@ export async function convertToInferred(tree: Tree, options: Schema) {
function postTargetTransformer(
target: TargetConfiguration,
tree: Tree
tree: Tree,
projectDetails: { projectName: string; root: string },
inferredTargetConfiguration: TargetConfiguration
): TargetConfiguration {
if (target.options) {
const configFilePath = target.options.cypressConfig;
delete target.options.cypressConfig;
delete target.options.copyFiles;
delete target.options.skipServe;
for (const key in targetOptionsToCliMap) {
if (target.options[key]) {
target.options[targetOptionsToCliMap[key]] = target.options[key];
delete target.options[key];
}
}
if ('exit' in target.options && !target.options.exit) {
delete target.options.exit;
target.options['no-exit'] = true;
}
if (target.options.testingType) {
delete target.options.testingType;
}
if (target.options.watch) {
target.options.headed = true;
target.options['no-exit'] = true;
delete target.options.watch;
}
if (target.options.baseUrl) {
upsertBaseUrl(tree, configFilePath, target.options.baseUrl);
delete target.options.baseUrl;
}
if (target.options.devServerTarget) {
const webServerCommands: Record<string, string> = {
default: `npx nx run ${target.options.devServerTarget}`,
};
delete target.options.devServerTarget;
if (target.configurations) {
for (const configuration in target.configurations) {
if (target.configurations[configuration]?.devServerTarget) {
webServerCommands[
configuration
] = `npx nx run ${target.configurations[configuration].devServerTarget}`;
delete target.configurations[configuration].devServerTarget;
}
}
}
addDevServerTargetToConfig(
tree,
configFilePath,
webServerCommands,
webServerCommands?.['ci']
);
}
if (target.options.ignoreTestFiles) {
addExcludeSpecPattern(
tree,
configFilePath,
target.options.ignoreTestFiles
);
delete target.options.ignoreTestFiles;
}
handlePropertiesInOptions(
tree,
target.options,
projectDetails.root,
target
);
if (Object.keys(target.options).length === 0) {
delete target.options;
}
if (
target.configurations &&
Object.keys(target.configurations).length !== 0
) {
}
if (target.configurations) {
for (const configurationName in target.configurations) {
const configuration = target.configurations[configurationName];
handlePropertiesInOptions(
tree,
configuration,
projectDetails.root,
target
);
}
if (Object.keys(target.configurations).length !== 0) {
for (const configuration in target.configurations) {
if (Object.keys(target.configurations[configuration]).length === 0) {
delete target.configurations[configuration];
@ -149,7 +105,94 @@ function postTargetTransformer(
}
}
if (target.outputs) {
processTargetOutputs(target, [], inferredTargetConfiguration, {
projectName: projectDetails.projectName,
projectRoot: projectDetails.root,
});
}
return target;
}
function handlePropertiesInOptions(
tree: Tree,
options: Record<string, any>,
projectRoot: string,
target: TargetConfiguration
) {
let configFilePath: string;
if ('cypressConfig' in options) {
configFilePath = options.cypressConfig;
options['config-file'] = toProjectRelativePath(configFilePath, projectRoot);
delete options.cypressConfig;
}
if ('copyFiles' in options) {
delete options.copyFiles;
}
if ('skipServe' in options) {
delete options.skipServe;
}
for (const key in targetOptionsToCliMap) {
if (options[key]) {
const prevValue = options[key];
delete options[key];
options[targetOptionsToCliMap[key]] = prevValue;
}
}
if ('exit' in options && !options.exit) {
delete options.exit;
options['no-exit'] = true;
}
if ('testingType' in options) {
delete options.testingType;
}
if ('watch' in options) {
options.headed = true;
options['no-exit'] = true;
delete options.watch;
}
if (options.baseUrl && configFilePath) {
upsertBaseUrl(tree, configFilePath, options.baseUrl);
delete options.baseUrl;
}
if (options.devServerTarget && configFilePath) {
const webServerCommands: Record<string, string> = {
default: `npx nx run ${options.devServerTarget}`,
};
delete options.devServerTarget;
if (target.configurations && configFilePath) {
for (const configuration in target.configurations) {
if (target.configurations[configuration]?.devServerTarget) {
webServerCommands[
configuration
] = `npx nx run ${target.configurations[configuration].devServerTarget}`;
delete target.configurations[configuration].devServerTarget;
}
}
}
addDevServerTargetToConfig(
tree,
configFilePath,
webServerCommands,
webServerCommands?.['ci']
);
}
if ('ignoreTestFiles' in options) {
addExcludeSpecPattern(tree, configFilePath, options.ignoreTestFiles);
delete options.ignoreTestFiles;
}
}
export default convertToInferred;

View File

@ -354,10 +354,6 @@ describe('Eslint - Convert Executors To Plugin', () => {
await convertToInferred(tree, { project: 'myapp', skipFormat: true });
// ASSERT
// project.json modifications
const updatedProject = readProjectConfiguration(tree, project.name);
const targetKeys = Object.keys(updatedProject.targets);
expect(targetKeys).not.toContain('lint');
// nx.json modifications
const nxJsonPlugins = readNxJson(tree).plugins;
@ -426,17 +422,10 @@ describe('Eslint - Convert Executors To Plugin', () => {
await convertToInferred(tree, { project: 'myapp', skipFormat: true });
// ASSERT
// project.json modifications
const updatedProject = readProjectConfiguration(tree, project.name);
const targetKeys = Object.keys(updatedProject.targets);
expect(targetKeys).not.toContain('lint');
const projectJsonForProject = readJson(
tree,
`${project.root}/project.json`
);
expect(projectJsonForProject['// targets']).toEqual(
'to see all targets run: nx show project myapp --web'
);
// nx.json modifications
const nxJsonPlugins = readNxJson(tree).plugins;
const addedTestEslintPlugin = nxJsonPlugins.find((plugin) => {
@ -580,10 +569,6 @@ describe('Eslint - Convert Executors To Plugin', () => {
await convertToInferred(tree, { skipFormat: true });
// ASSERT
// project.json modifications
const updatedProject = readProjectConfiguration(tree, project.name);
const targetKeys = Object.keys(updatedProject.targets);
expect(targetKeys).not.toContain('lint');
// nx.json modifications
const nxJsonPlugins = readNxJson(tree).plugins;
@ -610,10 +595,6 @@ describe('Eslint - Convert Executors To Plugin', () => {
await convertToInferred(tree, { skipFormat: true });
// ASSERT
// project.json modifications
const updatedProject = readProjectConfiguration(tree, project.name);
const targetKeys = Object.keys(updatedProject.targets);
['eslint'].forEach((key) => expect(targetKeys).not.toContain(key));
// nx.json modifications
const nxJsonPlugins = readNxJson(tree).plugins;
@ -641,10 +622,6 @@ describe('Eslint - Convert Executors To Plugin', () => {
await convertToInferred(tree, { skipFormat: true });
// ASSERT
// project.json modifications
const updatedProject = readProjectConfiguration(tree, project.name);
const targetKeys = Object.keys(updatedProject.targets);
expect(targetKeys).not.toContain('eslint');
// nx.json modifications
const nxJsonPlugins = readNxJson(tree).plugins;
@ -695,10 +672,6 @@ describe('Eslint - Convert Executors To Plugin', () => {
await convertToInferred(tree, { skipFormat: true });
// ASSERT
// project.json modifications
const updatedProject = readProjectConfiguration(tree, project.name);
const targetKeys = Object.keys(updatedProject.targets);
expect(targetKeys).not.toContain('eslint');
// nx.json modifications
const nxJsonPlugins = readNxJson(tree).plugins;
@ -744,12 +717,13 @@ describe('Eslint - Convert Executors To Plugin', () => {
// project.json modifications
const updatedProject = readProjectConfiguration(tree, project.name);
expect(updatedProject.targets.lint).toMatchInlineSnapshot(`
{
"options": {
"cache-location": "cache-dir",
},
}
`);
{
"options": {
"cache-location": "cache-dir",
"config": ".eslintrc.json",
},
}
`);
// nx.json modifications
const nxJsonPlugins = readNxJson(tree).plugins;
@ -785,12 +759,13 @@ describe('Eslint - Convert Executors To Plugin', () => {
// project.json modifications
const updatedProject = readProjectConfiguration(tree, project.name);
expect(updatedProject.targets.lint).toMatchInlineSnapshot(`
{
"options": {
"max-warnings": 10,
},
}
`);
{
"options": {
"config": ".eslintrc.json",
"max-warnings": 10,
},
}
`);
// nx.json modifications
const nxJsonPlugins = readNxJson(tree).plugins;

View File

@ -9,6 +9,10 @@ import { createNodesV2, EslintPluginOptions } from '../../plugins/plugin';
import { migrateExecutorToPlugin } from '@nx/devkit/src/generators/plugin-migrations/executor-to-plugin-migrator';
import { targetOptionsToCliMap } from './lib/target-options-map';
import { interpolate } from 'nx/src/tasks-runner/utils';
import {
processTargetOutputs,
toProjectRelativePath,
} from '@nx/devkit/src/generators/plugin-migrations/plugin-migration-utils';
interface Schema {
project?: string;
@ -56,7 +60,8 @@ export async function convertToInferred(tree: Tree, options: Schema) {
function postTargetTransformer(
target: TargetConfiguration,
tree: Tree,
projectDetails: { projectName: string; root: string }
projectDetails: { projectName: string; root: string },
inferredTargetConfiguration: TargetConfiguration
): TargetConfiguration {
if (target.inputs) {
const inputs = target.inputs.filter(
@ -75,63 +80,101 @@ function postTargetTransformer(
}
if (target.options) {
if ('eslintConfig' in target.options) {
delete target.options.eslintConfig;
handlePropertiesInOptions(target.options, projectDetails, target);
}
if (target.configurations) {
for (const configurationName in target.configurations) {
const configuration = target.configurations[configurationName];
handlePropertiesInOptions(configuration, projectDetails, target);
}
if ('force' in target.options) {
delete target.options.force;
}
if ('silent' in target.options) {
delete target.options.silent;
}
if ('hasTypeAwareRules' in target.options) {
delete target.options.hasTypeAwareRules;
}
if ('errorOnUnmatchedPattern' in target.options) {
if (!target.options.errorOnUnmatchedPattern) {
target.options['no-error-on-unmatched-pattern'] = true;
}
delete target.options.errorOnUnmatchedPattern;
}
if ('outputFile' in target.options) {
target.outputs ??= [];
target.outputs.push(target.options.outputFile);
}
for (const key in targetOptionsToCliMap) {
if (target.options[key]) {
target.options[targetOptionsToCliMap[key]] = target.options[key];
delete target.options[key];
}
}
if ('lintFilePatterns' in target.options) {
const normalizedLintFilePatterns = target.options.lintFilePatterns.map(
(pattern) => {
return interpolate(pattern, {
workspaceRoot: '',
projectRoot: projectDetails.root,
projectName: projectDetails.projectName,
});
if (Object.keys(target.configurations).length !== 0) {
for (const configuration in target.configurations) {
if (Object.keys(target.configurations[configuration]).length === 0) {
delete target.configurations[configuration];
}
);
target.options.args = normalizedLintFilePatterns.map((pattern) =>
pattern.startsWith(projectDetails.root)
? pattern.replace(new RegExp(`^${projectDetails.root}/`), './')
: pattern
);
delete target.options.lintFilePatterns;
}
if (Object.keys(target.configurations).length === 0) {
delete target.configurations;
}
}
}
if (target.outputs) {
processTargetOutputs(target, [], inferredTargetConfiguration, {
projectName: projectDetails.projectName,
projectRoot: projectDetails.root,
});
}
return target;
}
function handlePropertiesInOptions(
options: Record<string, any>,
projectDetails: { projectName: string; root: string },
target: TargetConfiguration
) {
if ('eslintConfig' in options) {
options.config = toProjectRelativePath(
options.eslintConfig,
projectDetails.root
);
delete options.eslintConfig;
}
if ('force' in options) {
delete options.force;
}
if ('silent' in options) {
delete options.silent;
}
if ('hasTypeAwareRules' in options) {
delete options.hasTypeAwareRules;
}
if ('errorOnUnmatchedPattern' in options) {
if (!options.errorOnUnmatchedPattern) {
options['no-error-on-unmatched-pattern'] = true;
}
delete options.errorOnUnmatchedPattern;
}
if ('outputFile' in options) {
target.outputs ??= [];
target.outputs.push(options.outputFile);
}
for (const key in targetOptionsToCliMap) {
if (options[key]) {
const prevValue = options[key];
delete options[key];
options[targetOptionsToCliMap[key]] = prevValue;
}
}
if ('lintFilePatterns' in options) {
const normalizedLintFilePatterns = options.lintFilePatterns.map(
(pattern) => {
return interpolate(pattern, {
workspaceRoot: '',
projectRoot: projectDetails.root,
projectName: projectDetails.projectName,
});
}
);
options.args = normalizedLintFilePatterns.map((pattern) =>
pattern.startsWith(projectDetails.root)
? pattern.replace(new RegExp(`^${projectDetails.root}/`), './')
: pattern
);
delete options.lintFilePatterns;
}
}
export default convertToInferred;

View File

@ -45,14 +45,25 @@ function postTargetTransformer(
delete target.options.config;
}
for (const [key, value] of Object.entries(target.options)) {
const newKeyName = names(key).fileName;
delete target.options[key];
target.options[newKeyName] = value;
handleRenameOfProperties(target.options);
}
if (target.configurations) {
for (const configurationName in target.configurations) {
const configuration = target.configurations[configurationName];
handleRenameOfProperties(configuration);
}
}
return target;
}
function handleRenameOfProperties(options: Record<string, unknown>) {
for (const [key, value] of Object.entries(options)) {
const newKeyName = names(key).fileName;
delete options[key];
options[newKeyName] = value;
}
}
export default convertToInferred;

View File

@ -0,0 +1,241 @@
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { extractRollupConfigFromExecutorOptions } from './extract-rollup-config-from-executor-options';
describe('extract-rollup-config-from-executor-options', () => {
it('should extract the options correctly', () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace();
tree.write(
`apps/myapp/rollup.config.js`,
`export default (config) => {return config;}`
);
// ACT
const defaultValues = extractRollupConfigFromExecutorOptions(
tree,
{
outputPath: 'dist/apps/myapp',
tsConfig: 'apps/myapp/tsconfig.json',
project: '',
main: 'src/lib/index.ts',
rollupConfig: 'apps/myapp/rollup.config.js',
watch: true,
format: ['esm', 'cjs'],
},
{},
'apps/myapp'
);
// ASSERT
const configFile = tree.read('apps/myapp/rollup.config.js', 'utf-8');
expect(configFile).toMatchInlineSnapshot(`
"const { withNx } = require('@nx/rollup/with-nx');
// These options were migrated by @nx/rollup:convert-to-inferred from project.json
const options = {
"outputPath": "../../dist/apps/myapp",
"tsConfig": "./tsconfig.json",
"project": "",
"main": "../../src/lib/index.ts",
"format": [
"esm",
"cjs"
]
};
let config = withNx(options, {
// Provide additional rollup configuration here. See: https://rollupjs.org/configuration-options
// e.g.
// output: { sourcemap: true },
});
config = require('./rollup.migrated.config.js')(config, options);
module.exports = config;"
`);
expect(tree.exists('apps/myapp/rollup.migrated.config.js')).toBeTruthy();
expect(defaultValues).toMatchInlineSnapshot(`
{
"format": [
"esm",
"cjs",
],
"main": "../../src/lib/index.ts",
"outputPath": "../../dist/apps/myapp",
"project": "",
"tsConfig": "./tsconfig.json",
}
`);
});
it('should extract configurations that do not defined a rollupConfig into the rollup.config.js file', () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace();
tree.write(
`apps/myapp/rollup.config.js`,
`export default (config) => {return config;}`
);
tree.write(
`apps/myapp/rollup.dev.config.js`,
`export default (config) => {return config;}`
);
// ACT
const defaultValues = extractRollupConfigFromExecutorOptions(
tree,
{
outputPath: 'dist/apps/myapp',
tsConfig: 'apps/myapp/tsconfig.json',
project: '',
main: 'src/lib/index.ts',
rollupConfig: 'apps/myapp/rollup.config.js',
watch: true,
format: ['esm', 'cjs'],
},
{
dev: {
format: ['esm'],
},
},
'apps/myapp'
);
// ASSERT
const configFile = tree.read('apps/myapp/rollup.config.js', 'utf-8');
expect(configFile).toMatchInlineSnapshot(`
"const { withNx } = require('@nx/rollup/with-nx');
// These options were migrated by @nx/rollup:convert-to-inferred from project.json
const configValues = {
"default": {
"outputPath": "../../dist/apps/myapp",
"tsConfig": "./tsconfig.json",
"project": "",
"main": "../../src/lib/index.ts",
"format": [
"esm",
"cjs"
]
},
"dev": {
"format": [
"esm"
]
}
};
// Determine the correct configValue to use based on the configuration
const nxConfiguration = process.env.NX_TASK_TARGET_CONFIGURATION ?? 'default';
const options = {
...configValues.default,
...configValues[nxConfiguration],
};
let config = withNx(options, {
// Provide additional rollup configuration here. See: https://rollupjs.org/configuration-options
// e.g.
// output: { sourcemap: true },
});
config = require('./rollup.migrated.config.js')(config, options);
module.exports = config;"
`);
expect(tree.exists('apps/myapp/rollup.migrated.config.js')).toBeTruthy();
expect(defaultValues).toMatchInlineSnapshot(`
{
"format": [
"esm",
"cjs",
],
"main": "../../src/lib/index.ts",
"outputPath": "../../dist/apps/myapp",
"project": "",
"tsConfig": "./tsconfig.json",
}
`);
});
it('should extract configurations that do defined a rollupConfig into their own rollup.{configName}.config.js file', () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace();
tree.write(
`apps/myapp/rollup.config.js`,
`export default (config) => {return config;}`
);
tree.write(
`apps/myapp/rollup.dev.config.js`,
`export default (config) => {return config;}`
);
// ACT
const defaultValues = extractRollupConfigFromExecutorOptions(
tree,
{
outputPath: 'dist/apps/myapp',
tsConfig: 'apps/myapp/tsconfig.json',
project: '',
main: 'src/lib/index.ts',
rollupConfig: 'apps/myapp/rollup.config.js',
watch: true,
format: ['esm', 'cjs'],
},
{
dev: {
rollupConfig: 'rollup.dev.config.js',
format: ['esm'],
},
},
'apps/myapp'
);
// ASSERT
const configFile = tree.read('apps/myapp/rollup.dev.config.js', 'utf-8');
expect(configFile).toMatchInlineSnapshot(`
"const { withNx } = require('@nx/rollup/with-nx');
// These options were migrated by @nx/rollup:convert-to-inferred from project.json
const configValues = {
"outputPath": "../../dist/apps/myapp",
"tsConfig": "./tsconfig.json",
"project": "",
"main": "../../src/lib/index.ts",
"format": [
"esm"
]
};
// Determine the correct configValue to use based on the configuration
const nxConfiguration = process.env.NX_TASK_TARGET_CONFIGURATION ?? 'default';
const options = {
...configValues.default,
...configValues[nxConfiguration],
};
let config = withNx(options, {
// Provide additional rollup configuration here. See: https://rollupjs.org/configuration-options
// e.g.
// output: { sourcemap: true },
});
config = require('./rollup.dev.migrated.config.js')(config, options);
module.exports = config;"
`);
expect(
tree.exists('apps/myapp/rollup.dev.migrated.config.js')
).toBeTruthy();
expect(defaultValues).toMatchInlineSnapshot(`
{
"format": [
"esm",
"cjs",
],
"main": "../../src/lib/index.ts",
"outputPath": "../../dist/apps/myapp",
"project": "",
"tsConfig": "./tsconfig.json",
}
`);
});
});

View File

@ -10,8 +10,6 @@ export function extractRollupConfigFromExecutorOptions(
) {
normalizePathOptions(projectRoot, options);
let newRollupConfigContent: string;
const hasConfigurations =
!!configurations && Object.keys(configurations).length > 0;
@ -40,26 +38,104 @@ export function extractRollupConfigFromExecutorOptions(
delete options[key];
defaultOptions[key] = value;
}
let configurationOptions: Record<string, Record<string, unknown>>;
if (hasConfigurations) {
const configurationOptions: Record<string, Record<string, unknown>> = {};
configurationOptions = {};
for (const [key, value] of Object.entries(configurations)) {
let newConfigFileName: string;
let oldRollupConfigForConfiguration: string[];
for (const [optionKey, optionValue] of Object.entries(value)) {
if (optionKey === 'watch') continue;
/**
* If a configuration lists rollupConfig as an option
* Collect the options and set up a new file to point to
* Set the `--config` option to the new file
*/
if (optionKey === 'rollupConfig') {
oldRollupConfigForConfiguration = Array.isArray(optionValue)
? optionValue
: optionValue
? [optionValue]
: [];
newConfigFileName = `rollup.${key}.config.js`;
for (let i = 0; i < oldRollupConfigForConfiguration.length; i++) {
const file = oldRollupConfigForConfiguration[i];
if (file === newConfigFileName) {
tree.rename(
joinPathFragments(projectRoot, newConfigFileName),
joinPathFragments(
projectRoot,
`rollup.${key}.migrated.config.js`
)
);
oldRollupConfigForConfiguration.splice(
i,
1,
`./rollup.${key}.migrated.config.js`
);
}
}
delete value[optionKey];
value['config'] = newConfigFileName;
continue;
}
delete value[optionKey];
configurationOptions[key] ??= {};
configurationOptions[key][optionKey] = optionValue;
}
}
newRollupConfigContent = stripIndents`
/**
* Only if we encountered a rollupConfig in the current configuration
* should we write a new config file, containing all the config values
*/
if (newConfigFileName) {
tree.write(
joinPathFragments(projectRoot, newConfigFileName),
createNewRollupConfig(
oldRollupConfigForConfiguration,
defaultOptions,
configurationOptions[key],
true
)
);
}
}
}
tree.write(
joinPathFragments(projectRoot, `rollup.config.js`),
createNewRollupConfig(oldRollupConfig, defaultOptions, configurationOptions)
);
return defaultOptions;
}
function createNewRollupConfig(
oldRollupConfig: string[],
defaultOptions: Record<string, unknown>,
configurationOptions?:
| Record<string, Record<string, unknown>>
| Record<string, unknown>,
singleConfiguration = false
) {
if (configurationOptions) {
return stripIndents`
const { withNx } = require('@nx/rollup/with-nx');
// These options were migrated by @nx/rollup:convert-to-inferred from project.json
const configValues = ${JSON.stringify(
{
default: defaultOptions,
...configurationOptions,
},
singleConfiguration
? {
...defaultOptions,
...configurationOptions,
}
: {
default: defaultOptions,
...configurationOptions,
},
null,
2
)};
@ -86,7 +162,7 @@ export function extractRollupConfigFromExecutorOptions(
module.exports = config;
`;
} else {
newRollupConfigContent = stripIndents`
return stripIndents`
const { withNx } = require('@nx/rollup/with-nx');
// These options were migrated by @nx/rollup:convert-to-inferred from project.json
@ -106,11 +182,4 @@ export function extractRollupConfigFromExecutorOptions(
module.exports = config;
`;
}
tree.write(
joinPathFragments(projectRoot, `rollup.config.js`),
newRollupConfigContent
);
return defaultOptions;
}

View File

@ -206,6 +206,134 @@ export default defineConfig({plugins: [nxViteTsPaths({ buildLibsFromSource: opti
});"
`;
exports[`buildPostTargetTransformer should move the AST options to each vite config file correctly for configurations 1`] = `
"/// <reference types='vitest' />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
// These options were migrated by @nx/vite:convert-to-inferred from the project.json file.
const configValues = {"default":{"buildLibsFromSource":true},"dev":{"buildLibsFromSource":true}};
// Determine the correct configValue to use based on the configuration
const nxConfiguration = process.env.NX_TASK_TARGET_CONFIGURATION ?? 'default';
const options = {
...configValues.default,
...(configValues[nxConfiguration] ?? {})
}
export default defineConfig({
root: __dirname,
cacheDir: '../../node_modules/.vite/apps/myapp',
server: {
port: 4200,
host: 'localhost',
},
preview: {
port: 4300,
host: 'localhost',
},
plugins: [react(), nxViteTsPaths({ buildLibsFromSource: options.buildLibsFromSource }),],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
build: {
outDir: '../../dist/apps/myapp',
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,
},
},
test: {
globals: true,
cache: {
dir: '../../node_modules/.vitest',
},
environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'],
coverage: {
reportsDirectory: '../../coverage/apps/myapp',
provider: 'v8',
},
},
});"
`;
exports[`buildPostTargetTransformer should move the AST options to each vite config file correctly for configurations 2`] = `
"/// <reference types='vitest' />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
// These options were migrated by @nx/vite:convert-to-inferred from the project.json file.
const configValues = {"default":{},"dev":{"buildLibsFromSource":true}};
// Determine the correct configValue to use based on the configuration
const nxConfiguration = process.env.NX_TASK_TARGET_CONFIGURATION ?? 'default';
const options = {
...configValues.default,
...(configValues[nxConfiguration] ?? {})
}
export default defineConfig({
root: __dirname,
cacheDir: '../../node_modules/.vite/apps/myapp',
server: {
port: 4200,
host: 'localhost',
},
preview: {
port: 4300,
host: 'localhost',
},
plugins: [react(), nxViteTsPaths({ buildLibsFromSource: options.buildLibsFromSource }),],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
build: {
outDir: '../../dist/apps/myapp',
reportCompressedSize: true,
commonjsOptions: {
transformMixedEsModules: true,
},
},
test: {
globals: true,
cache: {
dir: '../../node_modules/.vitest',
},
environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'],
coverage: {
reportsDirectory: '../../coverage/apps/myapp',
provider: 'v8',
},
},
});"
`;
exports[`buildPostTargetTransformer should remove the correct options and move the AST options to the vite config file correctly and remove outputs when they match inferred 1`] = `
"/// <reference types='vitest' />
import { defineConfig } from 'vite';

View File

@ -13,7 +13,7 @@ describe('buildPostTargetTransformer', () => {
outputs: ['{options.outputPath}'],
options: {
outputPath: 'build/apps/myapp',
configFile: 'vite.config.ts',
configFile: 'apps/myapp/vite.config.ts',
buildLibsFromSource: true,
skipTypeCheck: false,
watch: true,
@ -27,7 +27,7 @@ describe('buildPostTargetTransformer', () => {
outputs: ['{projectRoot}/{options.outDir}'],
};
tree.write('vite.config.ts', viteConfigFileV17);
tree.write('apps/myapp/vite.config.ts', viteConfigFileV17);
// ACT
const target = buildPostTargetTransformer(
@ -41,12 +41,74 @@ describe('buildPostTargetTransformer', () => {
);
// ASSERT
const configFile = tree.read('vite.config.ts', 'utf-8');
const configFile = tree.read('apps/myapp/vite.config.ts', 'utf-8');
expect(configFile).toMatchSnapshot();
expect(target).toMatchInlineSnapshot(`
{
"options": {
"config": "../../vite.config.ts",
"config": "./vite.config.ts",
"outDir": "../../build/apps/myapp",
"watch": true,
},
}
`);
});
it('should move the AST options to each vite config file correctly for configurations', () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace();
const targetConfiguration = {
outputs: ['{options.outputPath}'],
options: {
outputPath: 'build/apps/myapp',
configFile: 'apps/myapp/vite.config.ts',
buildLibsFromSource: true,
skipTypeCheck: false,
watch: true,
generatePackageJson: true,
includeDevDependenciesInPackageJson: false,
tsConfig: 'apps/myapp/tsconfig.json',
},
configurations: {
dev: {
configFile: 'apps/myapp/vite.dev.config.ts',
},
},
};
const inferredTargetConfiguration = {
outputs: ['{projectRoot}/{options.outDir}'],
};
tree.write('apps/myapp/vite.config.ts', viteConfigFileV17);
tree.write('apps/myapp/vite.dev.config.ts', viteConfigFileV17);
// ACT
const target = buildPostTargetTransformer(
targetConfiguration,
tree,
{
projectName: 'myapp',
root: 'apps/myapp',
},
inferredTargetConfiguration
);
// ASSERT
const configFile = tree.read('apps/myapp/vite.config.ts', 'utf-8');
expect(configFile).toMatchSnapshot();
const devConfigFile = tree.read('apps/myapp/vite.dev.config.ts', 'utf-8');
expect(devConfigFile).toMatchSnapshot();
expect(target).toMatchInlineSnapshot(`
{
"configurations": {
"dev": {
"config": "./vite.dev.config.ts",
},
},
"options": {
"config": "./vite.config.ts",
"outDir": "../../build/apps/myapp",
"watch": true,
},

View File

@ -1,4 +1,8 @@
import { type TargetConfiguration, type Tree } from '@nx/devkit';
import {
joinPathFragments,
type TargetConfiguration,
type Tree,
} from '@nx/devkit';
import { tsquery } from '@phenomnomnominal/tsquery';
import { extname } from 'path/posix';
import {
@ -20,35 +24,42 @@ export function buildPostTargetTransformer(
default: {},
};
if (target.options) {
if (target.options.configFile) {
viteConfigPath = target.options.configFile;
}
removePropertiesFromTargetOptions(
tree,
target.options,
viteConfigPath,
projectDetails.root,
configValues['default'],
true
);
}
if (target.configurations) {
for (const configurationName in target.configurations) {
const configuration = target.configurations[configurationName];
configValues[configurationName] = {};
let configurationConfigFile = viteConfigPath;
if (configuration.configFile) {
if ('buildLibsFromSource' in target.options) {
configuration.buildLibsFromSource =
target.options.buildLibsFromSource;
}
configurationConfigFile = configuration.configFile;
}
removePropertiesFromTargetOptions(
tree,
configuration,
viteConfigPath,
configurationConfigFile,
projectDetails.root,
configValues[configurationName]
configValues[configurationName],
configuration.configFile && configuration.configFile !== viteConfigPath
);
}
if (Object.keys(configuration).length === 0) {
delete target.configurations[configurationName];
for (const configurationName in target.configurations) {
const configuration = target.configurations[configurationName];
if (
configuration.config &&
configuration.config !==
toProjectRelativePath(viteConfigPath, projectDetails.root)
) {
const configFilePath = joinPathFragments(
projectDetails.root,
configuration.config
);
addConfigValuesToViteConfig(tree, configFilePath, configValues);
}
}
@ -67,6 +78,21 @@ export function buildPostTargetTransformer(
}
}
if (target.options) {
if (target.options.configFile) {
viteConfigPath = target.options.configFile;
}
removePropertiesFromTargetOptions(
tree,
target.options,
viteConfigPath,
projectDetails.root,
configValues['default'],
true
);
}
if (target.outputs) {
processTargetOutputs(
target,
@ -97,7 +123,7 @@ function removePropertiesFromTargetOptions(
viteConfigPath: string,
projectRoot: string,
configValues: Record<string, unknown>,
defaultOptions = false
needsAstTransform = false
) {
if ('configFile' in targetOptions) {
targetOptions.config = toProjectRelativePath(
@ -117,7 +143,7 @@ function removePropertiesFromTargetOptions(
if ('buildLibsFromSource' in targetOptions) {
configValues['buildLibsFromSource'] = targetOptions.buildLibsFromSource;
if (defaultOptions) {
if (needsAstTransform) {
moveBuildLibsFromSourceToViteConfig(tree, viteConfigPath);
}
delete targetOptions.buildLibsFromSource;

View File

@ -27,10 +27,6 @@ export function previewPostTargetTransformer(migrationLogs: AggregatedLog) {
projectDetails.projectName,
migrationLogs
);
if (Object.keys(configuration).length === 0) {
delete target.configurations[configurationName];
}
}
if (Object.keys(target.configurations).length === 0) {

View File

@ -34,10 +34,6 @@ export function servePostTargetTransformer(migrationLogs: AggregatedLog) {
projectDetails.projectName,
migrationLogs
);
if (Object.keys(configuration).length === 0) {
delete target.configurations[configurationName];
}
}
if (Object.keys(target.configurations).length === 0) {

View File

@ -16,10 +16,6 @@ export function testPostTargetTransformer(
for (const configurationName in target.configurations) {
const configuration = target.configurations[configurationName];
removePropertiesFromTargetOptions(configuration, projectDetails.root);
if (Object.keys(configuration).length === 0) {
delete target.configurations[configurationName];
}
}
if (Object.keys(target.configurations).length === 0) {