UNPKG

@department-of-veterans-affairs/generator-vets-website

Version:
297 lines (262 loc) 9.58 kB
'use strict'; const Generator = require('yeoman-generator'); const chalk = require('chalk'); const TEMPLATE_TYPES = { WITH_1_PAGE: 'WITH_1_PAGE', WITH_4_PAGES: 'WITH_4_PAGES', }; /** * Helper that returns the last day of next year * of M/D/YYYY * 12/31/2019 */ function getDate() { const now = new Date(); const year = now.getFullYear() + 1; return `12/31/${year}`; } module.exports = class extends Generator { prompting() { const prompts = [ { type: 'input', name: 'formNumber', message: "What's your form number? Examples: '22-0993' or '21P-530'", // 1. Replace spaces and underscores with dashes // 2. Convert to uppercase filter(val) { return val.replace(/\s|_/g, '-').toUpperCase(); }, validate(input) { if (input.trim() === '') { return 'Form number is required'; } if (input.match(/['"]/)) { return 'Form number cannot contain quotes'; } return true; }, }, { type: 'input', name: 'trackingPrefix', message: "What's the Google Analytics event prefix that you want to use? Examples: 'burials-530-' or 'edu-0993-'", default: `${this.options.entryName}-`, }, { type: 'input', name: 'respondentBurden', message: "What's the respondent burden of this form in minutes?", default: '30', }, { type: 'input', name: 'ombNumber', message: "What's the OMB control number for this form? Example: '2900-0797'", }, { type: 'input', name: 'expirationDate', message: "What's the OMB expiration date (in M/D/YYYY format) for this form? Example: '1/31/2019'", default: getDate, }, { type: 'input', name: 'benefitDescription', message: "What's the benefit description for this form? Examples: 'education benefits' or 'disability claims increase'", default: `benefits`, }, { type: 'confirm', name: 'usesVetsJsonSchema', message: 'Does this form use vets-json-schema? (JSON schemas defined in separate repository)', default: false, when: (props) => props.formNumber, }, { type: 'list', name: 'templateType', message: 'Which form template would you like to start with?', choices: [ `${TEMPLATE_TYPES.WITH_1_PAGE}: A form with 1 page - name and date of birth`, `${TEMPLATE_TYPES.WITH_4_PAGES}: A form with 4 pages - name and date of birth, identification information, mailing address, and phone and email`, ], filter: (choice) => choice.split(':')[0], }, ]; return this.prompt(prompts).then((props) => { this.props = { ...this.options, ...props }; this.props.formIdConst = `FORM_${this.props.formNumber.replace(/-/g, '_')}`; }); } writing() { const appPath = `src/applications/${this.props.folderName}`; this.fs.copyTpl( this.templatePath('entry.scss.ejs'), this.destinationPath(`${appPath}/sass/${this.props.entryName}.scss`), this.props, ); this.fs.copyTpl( this.templatePath('reducer.js.ejs'), this.destinationPath(`${appPath}/reducers/index.js`), this.props, ); this.fs.copyTpl( this.templatePath('App.jsx.ejs'), this.destinationPath(`${appPath}/containers/App.jsx`), this.props, ); this.fs.copyTpl( this.templatePath('routes.jsx.ejs'), this.destinationPath(`${appPath}/routes.jsx`), this.props, ); this.fs.copy(this.templatePath('tests'), this.destinationPath(`${appPath}/tests`)); this.fs.copyTpl( this.templatePath('cypress.spec.js.ejs'), this.destinationPath(`${appPath}/tests/${this.props.entryName}.cypress.spec.js`), this.props, ); this.fs.copyTpl( this.templatePath('IntroductionPage.jsx.ejs'), this.destinationPath(`${appPath}/containers/IntroductionPage.jsx`), this.props, ); this.fs.copyTpl( this.templatePath('ConfirmationPage.jsx.ejs'), this.destinationPath(`${appPath}/containers/ConfirmationPage.jsx`), this.props, ); this.fs.copyTpl( this.templatePath('pages/nameAndDateOfBirth.js.ejs'), this.destinationPath(`${appPath}/pages/nameAndDateOfBirth.js`), this.props, ); this.fs.copyTpl( this.templatePath('constants.js.ejs'), this.destinationPath(`${appPath}/constants.js`), this.props, ); this.fs.copyTpl( this.templatePath('form.js.ejs'), this.destinationPath(`${appPath}/config/form.js`), this.props, ); if (this.props.templateType === TEMPLATE_TYPES.WITH_4_PAGES) { this.fs.copyTpl( this.templatePath('pages/identificationInformation.js.ejs'), this.destinationPath(`${appPath}/pages/identificationInformation.js`), this.props, ); this.fs.copyTpl( this.templatePath('pages/mailingAddress.js.ejs'), this.destinationPath(`${appPath}/pages/mailingAddress.js`), this.props, ); this.fs.copyTpl( this.templatePath('pages/phoneAndEmailAddress.js.ejs'), this.destinationPath(`${appPath}/pages/phoneAndEmailAddress.js`), this.props, ); } this.regexFileReplacements(); } regexFileReplacements() { const tryUpdateRegexInFile = (filePath, regex, newEntry, detailMessage) => { const content = this.fs.read(filePath); try { const updatedContent = content.replace( regex, (match, start, arrayContent, end) => { if (arrayContent.includes(newEntry)) { return match; } return `${start}${arrayContent.trimEnd()}\n${newEntry}\n${end}`; }, ); this.fs.write(filePath, updatedContent); } catch { this.log(chalk.yellow(`Could not write to ${filePath}. ${detailMessage}`)); } }; const updateMissingJsonSchema = () => { if (!this.props.usesVetsJsonSchema) { const filePath = './src/platform/forms/tests/forms-config-validator.unit.spec.js'; const regex = /(const missingFromVetsJsonSchema = \[)([\s\S]*?)(\];)/; const newEntry = ` VA_FORM_IDS.${this.props.formIdConst},`; tryUpdateRegexInFile( filePath, regex, newEntry, 'Trying to update missingFromVetsJsonSchema.', ); } }; updateMissingJsonSchema(); if (this.props.formNumber) { const filePath = './src/platform/forms/constants.js'; let regex = /(export const VA_FORM_IDS = Object\.freeze\({)([\s\S]*?)(}\))/; let newEntry = ` ${this.props.formIdConst}: '${this.props.formNumber}',`; let detail = 'Trying to update VA_FORM_IDS.'; tryUpdateRegexInFile(filePath, regex, newEntry, detail); regex = /(export const FORM_BENEFITS = {)([\s\S]*?)(};)/; newEntry = ` [VA_FORM_IDS.${this.props.formIdConst}]: '${this.props.benefitDescription}',`; detail = 'Trying to update FORM_BENEFITS.'; tryUpdateRegexInFile(filePath, regex, newEntry, detail); regex = /(export const TRACKING_PREFIXES = {)([\s\S]*?)(};)/; newEntry = ` [VA_FORM_IDS.${this.props.formIdConst}]: '${this.props.trackingPrefix}',`; detail = 'Trying to update TRACKING_PREFIXES.'; tryUpdateRegexInFile(filePath, regex, newEntry, detail); regex = /(export const SIP_ENABLED_FORMS = new Set\(\[)([\s\S]*?)(]\);)/; newEntry = ` VA_FORM_IDS.${this.props.formIdConst},`; detail = 'Trying to update SIP_ENABLED_FORMS.'; tryUpdateRegexInFile(filePath, regex, newEntry, detail); regex = /(export const getAllFormLinks = [\s\S]*?return {)([\s\S]*?)( {2}};)/; newEntry = ` [VA_FORM_IDS.${this.props.formIdConst}]: \`\${tryGetAppUrl('${this.props.formNumber}')}/\`,`; detail = 'Trying to update getAllFormLinks.'; tryUpdateRegexInFile(filePath, regex, newEntry, detail); regex = /(export const MY_VA_SIP_FORMS = \[)([\s\S]*?)(];)/; newEntry = ` {\n` + ` id: VA_FORM_IDS.${this.props.formIdConst},\n` + ` benefit: '${this.props.benefitDescription}',\n` + ` title: '${this.props.appName}',\n` + ` description: '${this.props.benefitDescription}',\n` + ` trackingPrefix: '${this.props.trackingPrefix}',\n` + ` },`; detail = 'Trying to update MY_VA_SIP_FORMS.'; tryUpdateRegexInFile(filePath, regex, newEntry, detail); } } end() { process.nextTick(() => { this.log('------------------------------------'); this.log(chalk.bold('Commands:')); this.log( chalk.bold(`Site: `) + chalk.cyan(`http://localhost:3001${this.props.rootUrl}`), ); this.log( chalk.bold(`Watch: `) + chalk.cyan(`yarn watch --env entry=${this.props.entryName}`), ); this.log( chalk.bold(`Mock API: `) + chalk.cyan( `yarn mock-api --responses src/applications/${this.props.folderName}/tests/fixtures/mocks/local-mock-responses.js`, ), ); this.log( chalk.bold(`Unit test: `) + chalk.cyan( `yarn test:unit --app-folder ${this.props.folderName} --log-level all`, ), ); this.log('------------------------------------'); }); } };