Skip to content

Commit

Permalink
feat(CLI Onboarding): Support --name CLI option
Browse files Browse the repository at this point in the history
  • Loading branch information
pgrzesik committed May 13, 2021
1 parent 1ccc4d2 commit 5c25c7a
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 11 deletions.
3 changes: 3 additions & 0 deletions lib/cli/commands-schema/no-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ commands.set('', {
'template-path': {
usage: 'Template local path for the service.',
},
'name': {
usage: 'Name for the service.',
},
},
lifecycleEvents: ['initializeService', 'setupAws', 'autoUpdate', 'tabCompletion', 'end'],
});
Expand Down
52 changes: 41 additions & 11 deletions lib/cli/interactive-setup/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const createFromTemplate = require('../../utils/createFromTemplate');
const resolveVariables = require('../../configuration/variables');
const { confirm } = require('./utils');
const createFromLocalTemplate = require('../../utils/create-from-local-template');
const ServerlessError = require('../../serverless-error');

const isValidServiceName = RegExp.prototype.test.bind(/^[a-zA-Z][a-zA-Z0-9-]{0,100}$/);

Expand All @@ -29,6 +30,12 @@ const projectTypeChoice = async () =>
})
).projectType;

const INVALID_PROJECT_NAME_MESSAGE =
'Project name is not valid.\n' +
' - It should only contain alphanumeric and hyphens.\n' +
' - It should start with an alphabetic character.\n' +
" - Shouldn't exceed 128 characters";

const projectNameInput = async (workingDir) =>
(
await inquirer.prompt({
Expand All @@ -38,12 +45,7 @@ const projectNameInput = async (workingDir) =>
validate: async (input) => {
input = input.trim();
if (!isValidServiceName(input)) {
return (
'Project name is not valid.\n' +
' - It should only contain alphanumeric and hyphens.\n' +
' - It should start with an alphabetic character.\n' +
" - Shouldn't exceed 128 characters"
);
return INVALID_PROJECT_NAME_MESSAGE;
}

try {
Expand All @@ -56,25 +58,53 @@ const projectNameInput = async (workingDir) =>
})
).projectName.trim();

const resolveProjectNameInput = async (options, workingDir) => {
if (options.name) {
if (!isValidServiceName(options.name)) {
throw new ServerlessError(INVALID_PROJECT_NAME_MESSAGE, 'INVALID_PROJECT_NAME');
}

let alreadyTaken = false;
try {
await fs.promises.access(join(workingDir, options.name));
alreadyTaken = true;
} catch {
// Pass
}

if (alreadyTaken) {
throw new ServerlessError(
`Path ${options.name} is already taken`,
'TARGET_FOLDER_ALREADY_EXISTS'
);
}

return options.name;
}

return projectNameInput(workingDir);
};

module.exports = {
isApplicable({ serviceDir }) {
return !serviceDir;
},
async run(context) {
const workingDir = context.cwd || process.cwd();
const options = context.options || {};
const isConfirmed = await confirm('No project detected. Do you want to create a new one?', {
name: 'shouldCreateNewProject',
});
if (!isConfirmed) return;

// TODO: CLEANUP
let projectDir;
// TOOD: CLEANUP
let projectName;
if (context.options && context.options['template-path']) {
projectName = await projectNameInput(workingDir);
if (options['template-path']) {
projectName = await resolveProjectNameInput(options, workingDir);
projectDir = join(workingDir, projectName);
createFromLocalTemplate({
templatePath: context.options['template-path'],
templatePath: options['template-path'],
projectDir,
projectName,
});
Expand All @@ -87,7 +117,7 @@ module.exports = {
);
return;
}
projectName = await projectNameInput(workingDir);
projectName = await resolveProjectNameInput(options, workingDir);
projectDir = join(workingDir, projectName);
await createFromTemplate(projectType, projectDir);
}
Expand Down
33 changes: 33 additions & 0 deletions test/unit/lib/cli/interactive-setup/service.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ describe('test/unit/lib/cli/interactive-setup/service.test.js', () => {
const stats = await fsp.lstat('test-project-from-local-template/serverless.yml');
expect(stats.isFile()).to.be.true;
});

it('Should create project at not existing directory with provided `name`', async () => {
configureInquirerStub(inquirer, {
confirm: { shouldCreateNewProject: true },
list: { projectType: 'aws-nodejs' },
});
await step.run({ options: { name: 'test-project-from-cli-option' } });
const stats = await fsp.lstat('test-project-from-cli-option/serverless.yml');
expect(stats.isFile()).to.be.true;
});
});

it('Should not allow project creation in a directory in which already service is configured', async () => {
Expand All @@ -81,6 +91,19 @@ describe('test/unit/lib/cli/interactive-setup/service.test.js', () => {
);
});

it('Should not allow project creation in a directory in which already service is configured when `name` flag provided', async () => {
configureInquirerStub(inquirer, {
confirm: { shouldCreateNewProject: true },
list: { projectType: 'aws-nodejs' },
});

await fsp.mkdir('anotherexisting');

await expect(
step.run({ options: { name: 'anotherexisting' } })
).to.eventually.be.rejected.and.have.property('code', 'TARGET_FOLDER_ALREADY_EXISTS');
});

it('Should not allow project creation using an invalid project name', async () => {
configureInquirerStub(inquirer, {
confirm: { shouldCreateNewProject: true },
Expand All @@ -92,4 +115,14 @@ describe('test/unit/lib/cli/interactive-setup/service.test.js', () => {
'INVALID_ANSWER'
);
});

it('Should not allow project creation using an invalid project name when `name` flag provided', async () => {
configureInquirerStub(inquirer, {
confirm: { shouldCreateNewProject: true },
list: { projectType: 'aws-nodejs' },
});
await expect(
step.run({ options: { name: 'elo grzegżółka' } })
).to.eventually.be.rejected.and.have.property('code', 'INVALID_PROJECT_NAME');
});
});

0 comments on commit 5c25c7a

Please sign in to comment.