diff --git a/boilerplate/app/modules/entities/entities-screen.js b/boilerplate/app/modules/entities/entities-screen.js index 339077ef7..f150a763b 100644 --- a/boilerplate/app/modules/entities/entities-screen.js +++ b/boilerplate/app/modules/entities/entities-screen.js @@ -2,9 +2,10 @@ import React from 'react' import { ScrollView, Text } from 'react-native' import { connect } from 'react-redux' // Styles -/*eslint-disable no-unused-vars*/ +/* eslint-disable no-unused-vars */ import RoundedButton from '../../shared/components/rounded-button/rounded-button' import { + loginScreen, // ignite-jhipster-entity-screen-import-needle } from '../../navigation/layouts' /* eslint-enable */ diff --git a/package-lock.json b/package-lock.json index d7c604800..b2e2db59c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6468,6 +6468,11 @@ "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", "dev": true }, + "prettier": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz", + "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==" + }, "pretty-format": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", diff --git a/package.json b/package.json index b6bd36948..ce434f2a0 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "insight": "0.10.3", "jhipster-core": "4.3.0", "pluralize": "8.0.0", + "prettier": "1.18.2", "ramda": "0.26.1", "rimraf": "3.0.0", "semver": "6.3.0" diff --git a/src/boilerplate/files.js b/src/boilerplate/files.js index 293246136..2ed816d74 100644 --- a/src/boilerplate/files.js +++ b/src/boilerplate/files.js @@ -5,8 +5,9 @@ const semver = require('semver') * The files portion of the entity generator */ module.exports = async function (context, props, jhipsterConfig) { - const { filesystem, ignite, print, strings } = context + const { filesystem, print, strings } = context const { camelCase, upperFirst } = strings + const { copyBatch } = require('../lib/copy-batch') const spinner = print.spin(`using the ${print.colors.blue('JHipster')} boilerplate`).succeed() const { patchInFile } = require('../lib/patch-in-file') @@ -169,7 +170,7 @@ module.exports = async function (context, props, jhipsterConfig) { } ] - await ignite.copyBatch(context, templates, props, { + await copyBatch(context, templates, props, { quiet: true, directory: `${__dirname}/../../boilerplate` }) @@ -184,7 +185,7 @@ module.exports = async function (context, props, jhipsterConfig) { if (!isMonolith) { oauth2Files.push({ template: 'OAuth2SsoConfiguration.java.ejs', target: `${jhipsterPathPrefix}${props.jhipsterDirectory}/src/main/java/${props.packageFolder}/config/OAuth2SsoConfiguration.java` }) } - await ignite.copyBatch(context, oauth2Files, props, { + await copyBatch(context, oauth2Files, props, { quiet: true, directory: `${__dirname}/../../templates/jhipster/oauth2` }) @@ -213,7 +214,6 @@ module.exports = async function (context, props, jhipsterConfig) { await patchInFile(context, 'android/app/build.gradle', { before: 'applicationId', insert: androidAuthRedirectContent, - match: androidAuthRedirectContent }) } else { // remove OAuth2 files if not enabled @@ -230,7 +230,6 @@ module.exports = async function (context, props, jhipsterConfig) { await patchInFile(context, 'app/shared/reducers/index.js', { before: 'ignite-jhipster-redux-store-import-needle', insert: ` chat: require('../../modules/chat/chat.reducer').reducer,`, - match: ` chat: require('../../modules/chat/chat.reducer').reducer,` }) // TODO CHAT SCREEN diff --git a/src/boilerplate/index.js b/src/boilerplate/index.js index be1b46756..f3ca7124d 100644 --- a/src/boilerplate/index.js +++ b/src/boilerplate/index.js @@ -254,6 +254,12 @@ async function install (context) { await importEntityJdl.run(context) } + // run prettier to pass lint on generation + spinner.text = `▸ running prettier on generated code` + spinner.start() + // install any missing dependencies + await system.run(`${useNpm ? 'npm' : 'yarn'} run prettier`, { stdio: 'ignore' }) + const perfDuration = parseInt(((new Date()).getTime() - perfStart) / 10) / 100 spinner.succeed(`ignited ${print.colors.yellow(name)} in ${perfDuration}s`) diff --git a/src/entity/files.js b/src/entity/files.js index a5ff7fab3..cf80aac77 100644 --- a/src/entity/files.js +++ b/src/entity/files.js @@ -1,13 +1,15 @@ /** * The files portion of the entity generator */ + module.exports = async function (generator, igniteContext) { const semver = require('semver') const pluralize = require('pluralize') const fs = require('fs-extra') const { patchInFile } = require('../lib/patch-in-file') const { getEntityFormField, getRelationshipFormField } = require('../lib/entity-helpers') - const { ignite, strings, parameters, print } = igniteContext + const { copyBatch } = require('../lib/copy-batch') + const { strings, parameters, print } = igniteContext const { kebabCase, pascalCase, snakeCase, upperCase, camelCase, isBlank, upperFirst } = strings // eslint-disable-line const name = generator.name @@ -100,34 +102,33 @@ module.exports = async function (generator, igniteContext) { // REDUX AND SAGA SECTION let apiMethods = ` - const get${props.name} = (${camelCase(props.name)}Id) => api.get('${props.microservicePath}api/${kebabCase(props.pluralName)}/' + ${camelCase(props.name)}Id) - const get${props.pluralName} = (options) => api.get('${props.microservicePath}api/${kebabCase(props.pluralName)}', options) - const create${props.name} = (${camelCase(props.name)}) => api.post('${props.microservicePath}api/${kebabCase(props.pluralName)}', ${camelCase(props.name)}) - const update${props.name} = (${camelCase(props.name)}) => api.put('${props.microservicePath}api/${kebabCase(props.pluralName)}', ${camelCase(props.name)}) - const delete${props.name} = (${camelCase(props.name)}Id) => api.delete('${props.microservicePath}api/${kebabCase(props.pluralName)}/' + ${camelCase(props.name)}Id)` + const get${props.name} = ${camelCase(props.name)}Id => api.get('${props.microservicePath}api/${kebabCase(props.pluralName)}/' + ${camelCase(props.name)}Id) + const get${props.pluralName} = options => api.get('${props.microservicePath}api/${kebabCase(props.pluralName)}', options) + const create${props.name} = ${camelCase(props.name)} => api.post('${props.microservicePath}api/${kebabCase(props.pluralName)}', ${camelCase(props.name)}) + const update${props.name} = ${camelCase(props.name)} => api.put('${props.microservicePath}api/${kebabCase(props.pluralName)}', ${camelCase(props.name)}) + const delete${props.name} = ${camelCase(props.name)}Id => api.delete('${props.microservicePath}api/${kebabCase(props.pluralName)}/' + ${camelCase(props.name)}Id)` - let fixtureApiMethods = ` - update${props.name}: (${camelCase(props.name)}) => { + let fixtureApiMethods = ` update${props.name}: ${camelCase(props.name)} => { return { ok: true, - data: require('../../shared/fixtures/update-${props.name.toLowerCase()}.json') + data: require('../../shared/fixtures/update-${props.name.toLowerCase()}.json'), } }, get${props.pluralName}: () => { return { ok: true, - data: require('../../shared/fixtures/get-${props.pluralName.toLowerCase()}.json') + data: require('../../shared/fixtures/get-${props.pluralName.toLowerCase()}.json'), } }, - get${props.name}: (${camelCase(props.name)}Id) => { + get${props.name}: ${camelCase(props.name)}Id => { return { ok: true, - data: require('../../shared/fixtures/get-${props.name.toLowerCase()}.json') + data: require('../../shared/fixtures/get-${props.name.toLowerCase()}.json'), } }, - delete${props.name}: (${camelCase(props.name)}Id) => { + delete${props.name}: ${camelCase(props.name)}Id => { return { - ok: true + ok: true, } },` @@ -167,42 +168,35 @@ module.exports = async function (generator, igniteContext) { await patchInFile(igniteContext, apiFilePath, { before: 'ignite-jhipster-api-method-needle', insert: apiMethods, - match: apiMethods }) await patchInFile(igniteContext, apiFilePath, { before: 'ignite-jhipster-api-export-needle', insert: apiMethodsExport, - match: apiMethodsExport }) await patchInFile(igniteContext, fixtureApiFilePath, { before: 'ignite-jhipster-api-fixture-needle', insert: fixtureApiMethods, - match: fixtureApiMethods }) // import redux in redux/index.js await patchInFile(igniteContext, reduxIndexFilePath, { before: 'ignite-jhipster-redux-store-import-needle', insert: ` ${camelCase(props.pluralName)}: require('../../modules/entities/${props.kebabName}/${props.kebabName}.reducer').reducer,`, - match: ` ${camelCase(props.pluralName)}: require('../../modules/entities/${props.kebabName}/${props.kebabName}.reducer').reducer,` }) // import saga/redux in sagas/index.js await patchInFile(igniteContext, sagaIndexFilePath, { before: 'ignite-jhipster-saga-redux-import-needle', insert: `import { ${props.name}Types } from '../../modules/entities/${props.kebabName}/${props.kebabName}.reducer'`, - match: `import { ${props.name}Types } from '../../modules/entities/${props.kebabName}/${props.kebabName}.reducer'` }) await patchInFile(igniteContext, sagaIndexFilePath, { before: 'ignite-jhipster-saga-method-import-needle', insert: `import { get${props.name}, get${props.pluralName}, update${props.name}, delete${props.name}${props.searchEngine ? `, search${props.pluralName}` : ''} } from '../../modules/entities/${props.kebabName}/${props.kebabName}.sagas'`, - match: `import { get${props.name}, get${props.pluralName}, update${props.name}, delete${props.name}${props.searchEngine ? `, search${props.pluralName}` : ''} } from '../../modules/entities/${props.kebabName}/${props.kebabName}.sagas'` }) await patchInFile(igniteContext, sagaIndexFilePath, { before: 'ignite-jhipster-saga-redux-connect-needle', insert: sagaConnections, - match: sagaConnections }) const entityFiles = [ @@ -277,7 +271,7 @@ module.exports = async function (generator, igniteContext) { }) } - await ignite.copyBatch(igniteContext, entityFiles, props, { + await copyBatch(igniteContext, entityFiles, props, { directory: `${__dirname}/../../templates/entity` }) @@ -286,19 +280,16 @@ module.exports = async function (generator, igniteContext) { await patchInFile(igniteContext, navigationRouterFilePath, { before: 'ignite-jhipster-navigation-import-needle', insert: navigationImport, - match: navigationImport }) const navigationImportDetail = `import ${props.name}EntityDetailScreen from '../modules/entities/${props.kebabName}/${props.kebabName}-entity-detail-screen'` await patchInFile(igniteContext, navigationRouterFilePath, { before: 'ignite-jhipster-navigation-import-needle', insert: navigationImportDetail, - match: navigationImportDetail }) const navigationImportEdit = `import ${props.name}EntityEditScreen from '../modules/entities/${props.kebabName}/${props.kebabName}-entity-edit-screen'` await patchInFile(igniteContext, navigationRouterFilePath, { before: 'ignite-jhipster-navigation-import-needle', - insert: navigationImportEdit, - match: navigationImportEdit + insert: navigationImportEdit }) const upperSnakeCaseName = upperCase(snakeCase(props.name + 'EntityScreen')).replace(/ /g, '_') const upperSnakeCaseNameEdit = upperCase(snakeCase(props.name + 'EntityEditScreen')).replace(/ /g, '_') @@ -308,124 +299,116 @@ module.exports = async function (generator, igniteContext) { const navigationDeclaration = `export const ${upperSnakeCaseName} = 'nav.${props.name}EntityScreen'` await patchInFile(igniteContext, navigationRouterFilePath, { before: 'ignite-jhipster-navigation-declaration-needle', - insert: navigationDeclaration, - match: navigationDeclaration + insert: navigationDeclaration }) const navigationDeclarationDetail = `export const ${upperSnakeCaseNameDetail} = 'nav.${props.name}EntityDetailScreen'` await patchInFile(igniteContext, navigationRouterFilePath, { before: 'ignite-jhipster-navigation-declaration-needle', - insert: navigationDeclarationDetail, - match: navigationDeclarationDetail + insert: navigationDeclarationDetail }) const navigationDeclarationEdit = `export const ${upperSnakeCaseNameEdit} = 'nav.${props.name}EntityEditScreen'` await patchInFile(igniteContext, navigationRouterFilePath, { before: 'ignite-jhipster-navigation-declaration-needle', - insert: navigationDeclarationEdit, - match: navigationDeclarationEdit + insert: navigationDeclarationEdit }) // add entity screens to navigation const navigationScreen = ` Navigation.registerComponentWithRedux(${upperSnakeCaseName}, () => ${props.name}EntityScreen, Provider, store)` await patchInFile(igniteContext, navigationRouterFilePath, { before: 'ignite-jhipster-navigation-registration-needle', - insert: navigationScreen, - match: navigationScreen + insert: navigationScreen }) const navigationScreenDetail = ` Navigation.registerComponentWithRedux(${upperSnakeCaseNameDetail}, () => ${props.name}EntityDetailScreen, Provider, store)` await patchInFile(igniteContext, navigationRouterFilePath, { before: 'ignite-jhipster-navigation-registration-needle', - insert: navigationScreenDetail, - match: navigationScreenDetail + insert: navigationScreenDetail }) const navigationScreenEdit = ` Navigation.registerComponentWithRedux(${upperSnakeCaseNameEdit}, () => ${props.name}EntityEditScreen, Provider, store)` await patchInFile(igniteContext, navigationRouterFilePath, { before: 'ignite-jhipster-navigation-registration-needle', - insert: navigationScreenEdit, - match: navigationScreenEdit + insert: navigationScreenEdit }) const navigationMethodMain = ` -export const ${camelCase(props.name)}EntityScreen = () => Navigation.push('center', { - component: { - name: ${upperSnakeCaseName}, - options: { - topBar: { - title: { - text: '${props.pluralName}', - color: Colors.snow +export const ${camelCase(props.name)}EntityScreen = () => + Navigation.push('center', { + component: { + name: ${upperSnakeCaseName}, + options: { + topBar: { + title: { + text: '${props.pluralName}', + color: Colors.snow, + }, + rightButtons: [ + { + id: 'createButton', + text: 'Create', + color: Colors.snow, + }, + ], }, - rightButtons: [ - { - id: 'createButton', - text: 'Create', - color: Colors.snow - } - ] - } - } - } -})` + }, + }, + })` const navigationMethodDetail = ` -export const ${camelCase(props.name)}EntityDetailScreen = (data) => Navigation.push('center', { - component: { - name: ${upperSnakeCaseNameDetail}, - passProps: { - data +export const ${camelCase(props.name)}EntityDetailScreen = data => + Navigation.push('center', { + component: { + name: ${upperSnakeCaseNameDetail}, + passProps: { + data, + }, + options: { + topBar: { + title: { + text: '${props.pluralName}', + color: Colors.snow, + }, + }, + }, }, - options: { - topBar: { - title: { - text: '${props.pluralName}', - color: Colors.snow - } - } - } - } -})` + })` const navigationMethodEdit = ` -export const ${camelCase(props.name)}EntityEditScreen = (data) => Navigation.push('center', { - component: { - name: ${upperSnakeCaseNameEdit}, - passProps: { - data +export const ${camelCase(props.name)}EntityEditScreen = data => + Navigation.push('center', { + component: { + name: ${upperSnakeCaseNameEdit}, + passProps: { + data, + }, + options: { + topBar: { + title: { + text: '${props.pluralName}', + color: Colors.snow, + }, + }, + }, }, - options: { - topBar: { - title: { - text: '${props.pluralName}', - color: Colors.snow - } - } - } - } -})` + })` await patchInFile(igniteContext, navigationRouterFilePath, { before: 'ignite-jhipster-navigation-method-needle', - insert: navigationMethodMain, - match: navigationMethodMain + insert: navigationMethodMain }) await patchInFile(igniteContext, navigationRouterFilePath, { before: 'ignite-jhipster-navigation-method-needle', - insert: navigationMethodEdit, - match: navigationMethodEdit + insert: navigationMethodEdit }) await patchInFile(igniteContext, navigationRouterFilePath, { before: 'ignite-jhipster-navigation-method-needle', - insert: navigationMethodDetail, - match: navigationMethodDetail + insert: navigationMethodDetail }) // add entity to entities screen - const entityScreenButton = ` ` + const entityScreenButton = ` ` await patchInFile(igniteContext, entityScreenFilePath, { before: 'ignite-jhipster-entity-screen-needle', - insert: entityScreenButton, - match: entityScreenButton + insert: entityScreenButton }) const entityScreenImport = ` ${camelCase(props.name)}EntityScreen,` await patchInFile(igniteContext, entityScreenFilePath, { before: 'ignite-jhipster-entity-screen-import-needle', - insert: entityScreenImport, - match: entityScreenImport + insert: entityScreenImport }) } diff --git a/src/lib/copy-batch.js b/src/lib/copy-batch.js new file mode 100644 index 000000000..c392f60d7 --- /dev/null +++ b/src/lib/copy-batch.js @@ -0,0 +1,21 @@ +// wrapped version of ignite's copyBatch to run prettier on files before generation +const { prettierTransformBatch } = require('./prettier-transform') +const copyBatch = async (context, files, props, copyBatchOptions) => { + const { ignite, print } = context + try { + await ignite.copyBatch(context, files, props, copyBatchOptions) + } catch (e) { + print.warning('Could not copy files, error:') + print.error(e) + } + try { + await prettierTransformBatch(files) + } catch (e) { + print.warning('Could not run prettier on files, error:') + print.error(e) + } +} + +module.exports = { + copyBatch, +} diff --git a/src/lib/prettier-transform.js b/src/lib/prettier-transform.js new file mode 100644 index 000000000..8397940f1 --- /dev/null +++ b/src/lib/prettier-transform.js @@ -0,0 +1,54 @@ +/** + * Copyright 2013-2019 the original author or authors from the JHipster project. + * + * This file is part of the JHipster project, see https://www.jhipster.tech/ + * for more information. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// transforms a file via prettier as it's generated +// modified from https://github.com/jhipster/generator-jhipster/blob/6871b52b2336709fde83fb0dccf7165b1e227fd2/generators/generator-transforms.js +const prettier = require('prettier') +const fs = require('fs') +const defaultOptions = { + printWidth: 140, + singleQuote: true, + useTabs: false, + tabWidth: 2, + // js and ts rules: + arrowParens: 'avoid', + // jsx and tsx rules: + jsxBracketSameLine: false +} +const prettierTransform = async (file) => { + /* resolve from the projects config */ + let options = await prettier.resolveConfig(file.target) + if (!options || Object.keys(options).length === 0) { + options = defaultOptions + } + const str = fs.readFileSync(file.target, 'utf8') + // for better errors + options.filepath = file.target + const data = prettier.format(str, options) + fs.writeFileSync(file.target, Buffer.from(data)) +} + +const prettierTransformBatch = async (files) => { + await Promise.all(files.map(file => prettierTransform(file))) +} + +module.exports = { + prettierTransform, + prettierTransformBatch +} diff --git a/src/lib/react-native-navigation.js b/src/lib/react-native-navigation.js index 6d26fb6c9..811bed49b 100644 --- a/src/lib/react-native-navigation.js +++ b/src/lib/react-native-navigation.js @@ -1,4 +1,5 @@ const { patchInFile } = require('../lib/patch-in-file') +const { copyBatch } = require('../lib/copy-batch') const patchReactNativeNavigation = async (context = {}, props) => { // REACT_NATIVE_NAVIGATION_VERSION @@ -22,7 +23,7 @@ const patchReactNativeNavigation = async (context = {}, props) => { if (props.authType === 'oauth2') { navigationFiles.push({ template: 'AppDelegate.h.ejs', target: `ios/${props.name}/AppDelegate.h` }) } - await ignite.copyBatch(context, navigationFiles, props, { + await copyBatch(context, navigationFiles, props, { quiet: true, directory: `${__dirname}/../../templates/react-native-navigation/` }) @@ -31,7 +32,7 @@ const patchReactNativeNavigation = async (context = {}, props) => { const rnnPatch = [ { template: 'patches/tcomb-form-native+0.6.20.patch', target: `patches/tcomb-form-native+0.6.20.patch` } ] - await ignite.copyBatch(context, rnnPatch, props, { + await copyBatch(context, rnnPatch, props, { quiet: true, directory: `${__dirname}/../../boilerplate` })