Skip to content
This repository has been archived by the owner on Dec 16, 2022. It is now read-only.

Commit

Permalink
feat(v3): Add support for option override (#11)
Browse files Browse the repository at this point in the history
* test: Add tests for option override

* feat: Create utils for option override

* feat: Add support for option override

* test: Test util function `camelCase`

* fix: Use kebab case version of arguments

* feat: Allow to disable dependency installation from command-line
  • Loading branch information
francoischalifour authored May 9, 2018
1 parent 6a9c189 commit 5063a0d
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 13 deletions.
39 changes: 26 additions & 13 deletions src/createInstantSearchApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ const installDependencies = require('../tasks/installDependencies');
const {
checkAppName,
checkAppPath,
getOptionsFromArguments,
isQuestionAsked,
isYarnAvailable,
getLatestInstantSearchVersion,
} = require('./utils');
const { version } = require('../package.json');

let cdPath;
let appPath;
let options = {};

program
.version(version)
Expand All @@ -31,8 +34,11 @@ program
'--main-attribute <mainAttribute>',
'The main searchable attribute of your index'
)
.action(dest => {
.option('--template <template>', 'The InstantSearch template to use')
.option('--no-installation', 'Ignore dependency installation')
.action((dest, opts) => {
cdPath = dest;
options = opts;
appPath = path.resolve(dest);
})
.parse(process.argv);
Expand Down Expand Up @@ -66,6 +72,7 @@ const templates = fs
.map(name => path.join(templatesFolder, name))
.filter(source => fs.lstatSync(source).isDirectory())
.map(source => path.basename(source));
const optionsFromArguments = getOptionsFromArguments(options.rawArgs);

const questions = [
{
Expand Down Expand Up @@ -102,8 +109,11 @@ const questions = [
name: 'template',
message: 'InstantSearch template',
choices: templates,
validate(input) {
return templates.includes(input);
},
},
];
].filter(question => isQuestionAsked({ question, args: optionsFromArguments }));

const appName = path.basename(appPath);
const packageManager = isYarnAvailable() ? 'yarn' : 'npm';
Expand All @@ -120,7 +130,8 @@ try {
console.log(`Creating a new InstantSearch app in ${chalk.green(cdPath)}.`);
console.log();

const answers = await inquirer.prompt(questions);
const promptAnswers = await inquirer.prompt(questions);
const answers = { ...optionsFromArguments, ...promptAnswers };

const config = {
...answers,
Expand All @@ -137,18 +148,20 @@ try {
const installCommand = packageManager === 'yarn' ? 'yarn' : 'npm install';
let hasInstalledDependencies = false;

try {
console.log();
console.log('📦 Installing dependencies...');
console.log();
if (program.installation) {
try {
console.log();
console.log('📦 Installing dependencies...');
console.log();

installDependencies({ appPath, initialDirectory, installCommand });
installDependencies({ appPath, initialDirectory, installCommand });

hasInstalledDependencies = true;
} catch (err) {
console.warn(
'⚠️ Dependencies could not have been installed. Please follow the commands below to proceed.'
);
hasInstalledDependencies = true;
} catch (err) {
console.warn(
'⚠️ Dependencies could not have been installed. Please follow the commands below to proceed.'
);
}
}

console.log();
Expand Down
43 changes: 43 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,46 @@ function checkAppPath(path) {
}
}

function camelCase(string) {
return string.replace(/-([a-z])/g, str => str[1].toUpperCase());
}

function getOptionsFromArguments(rawArgs) {
let argIndex = 0;

return rawArgs.reduce((allArgs, currentArg) => {
argIndex++;

if (!currentArg.startsWith('--')) {
return allArgs;
}

const argumentName = camelCase(currentArg.split('--')[1]);
const argumentValue = rawArgs[argIndex];

return {
...allArgs,
[argumentName]: argumentValue,
};
}, {});
}

function isQuestionAsked({ question, args }) {
for (const optionName in args) {
if (question.name === optionName) {
// Skip if the arg in the command is valid
if (question.validate && question.validate(args[optionName])) {
return false;
}
} else if (!question.validate) {
// Skip if the question is optional and not given in the command
return false;
}
}

return true;
}

function isYarnAvailable() {
try {
execSync('yarnpkg --version', { stdio: 'ignore' });
Expand All @@ -54,6 +94,9 @@ function getLatestInstantSearchVersion() {
module.exports = {
checkAppName,
checkAppPath,
getOptionsFromArguments,
isQuestionAsked,
isYarnAvailable,
getLatestInstantSearchVersion,
camelCase,
};
114 changes: 114 additions & 0 deletions src/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,117 @@ describe('checkAppPath', () => {
});
});
});

describe('getOptionsFromArguments', () => {
test('with a single command', () => {
expect(
utils.getOptionsFromArguments('cmd --appId APP_ID'.split(' '))
).toEqual({
appId: 'APP_ID',
});
});

test('with a multiple commands', () => {
expect(
utils.getOptionsFromArguments([
'cmd',
'--appId',
'APP_ID',
'--apiKey',
'API_KEY',
'--indexName',
'INDEX_NAME',
'--template',
'Vue InstantSearch',
])
).toEqual({
appId: 'APP_ID',
apiKey: 'API_KEY',
indexName: 'INDEX_NAME',
template: 'Vue InstantSearch',
});
});

test('with different commands', () => {
expect(
utils.getOptionsFromArguments(['yarn', 'start', '--appId', 'APP_ID'])
).toEqual({
appId: 'APP_ID',
});

expect(
utils.getOptionsFromArguments(['node', 'index', '--appId', 'APP_ID'])
).toEqual({
appId: 'APP_ID',
});

expect(
utils.getOptionsFromArguments([
'create-instantsearch-app',
'--appId',
'APP_ID',
])
).toEqual({
appId: 'APP_ID',
});
});
});

describe('isQuestionAsked', () => {
expect(
utils.isQuestionAsked({
question: { name: 'appId', validate: input => Boolean(input) },
args: { appId: undefined },
})
).toBe(true);

expect(
utils.isQuestionAsked({
question: { name: 'appId', validate: input => Boolean(input) },
args: { appId: 'APP_ID' },
})
).toBe(false);

expect(
utils.isQuestionAsked({
question: {
name: 'template',
validate: input => input !== 'InstantSearch.js',
},
args: { template: 'InstantSearch.js' },
})
).toBe(true);

expect(
utils.isQuestionAsked({
question: {
name: 'template',
validate: input => input === 'InstantSearch.js',
},
args: { template: 'InstantSearch.js' },
})
).toBe(false);

expect(
utils.isQuestionAsked({
question: {
name: 'mainAttribute',
},
args: { indexName: 'INDEX_NAME' },
})
).toBe(false);
});

describe('camelCase', () => {
test('with single word', () => {
expect(utils.camelCase('test')).toBe('test');
});

test('with caret-separated word', () => {
expect(utils.camelCase('app-id')).toBe('appId');
});

test('with caret-separated twice word', () => {
expect(utils.camelCase('instant-search-js')).toBe('instantSearchJs');
});
});

0 comments on commit 5063a0d

Please sign in to comment.