Nicholas Cunningham 33bfc51ec2
fix(node): add project name sanitization for Docker commands. (#31461)
This PR improves our Docker support by sanitizing project names for
compatibility with Docker commands and Linux systems.

closes: #31421
2025-06-04 15:18:51 -06:00

99 lines
2.6 KiB
TypeScript

import {
formatFiles,
generateFiles,
GeneratorCallback,
joinPathFragments,
readNxJson,
readProjectConfiguration,
runTasksInSerial,
Tree,
updateProjectConfiguration,
} from '@nx/devkit';
import { SetUpDockerOptions } from './schema';
import { join } from 'path';
function normalizeOptions(
tree: Tree,
setupOptions: SetUpDockerOptions
): SetUpDockerOptions {
return {
...setupOptions,
project: setupOptions.project ?? readNxJson(tree).defaultProject,
targetName: setupOptions.targetName ?? 'docker-build',
buildTarget: setupOptions.buildTarget ?? 'build',
};
}
function sanitizeProjectName(projectName: string): string {
return projectName
.toLowerCase()
.replace(/[@\/]/g, '-')
.replace(/[^a-z0-9._-]/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '');
}
function addDocker(tree: Tree, options: SetUpDockerOptions) {
const projectConfig = readProjectConfiguration(tree, options.project);
const outputPath =
projectConfig.targets[options.buildTarget]?.options['outputPath'];
if (!projectConfig) {
throw new Error(`Cannot find project configuration for ${options.project}`);
}
if (!outputPath && !options.outputPath) {
throw new Error(
`The output path for the project ${options.project} is not defined. Please provide it as an option to the generator.`
);
}
const sanitizedProjectName = sanitizeProjectName(options.project);
generateFiles(tree, join(__dirname, './files'), projectConfig.root, {
tmpl: '',
buildLocation: options.outputPath ?? outputPath,
project: options.project,
projectPath: projectConfig.root,
sanitizedProjectName,
});
}
export function updateProjectConfig(tree: Tree, options: SetUpDockerOptions) {
let projectConfig = readProjectConfiguration(tree, options.project);
// Use sanitized project name for Docker image tag
const sanitizedProjectName = sanitizeProjectName(options.project);
projectConfig.targets[`${options.targetName}`] = {
dependsOn: [`${options.buildTarget}`],
command: `docker build -f ${joinPathFragments(
projectConfig.root,
'Dockerfile'
)} . -t ${sanitizedProjectName}`,
};
updateProjectConfiguration(tree, options.project, projectConfig);
}
export async function setupDockerGenerator(
tree: Tree,
setupOptions: SetUpDockerOptions
) {
const tasks: GeneratorCallback[] = [];
const options = normalizeOptions(tree, setupOptions);
// Should check if the node project exists
addDocker(tree, options);
updateProjectConfig(tree, options);
if (!options.skipFormat) {
await formatFiles(tree);
}
return runTasksInSerial(...tasks);
}
export default setupDockerGenerator;