diff --git a/CHANGELOG.md b/CHANGELOG.md
index a1d9fbd7..cada4c61 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Solid React Application Generator
+## 0.6.1 (August 28, 2019)
+
+#### Generator
+
+##### Added
+* New prompt when creating a new application. The new prompt asks if the developer would like to install a full sample application or just a simple one-page application. This provides a way for people familiar with Solid to create new blank applications and get started quicker.
+
## 0.6.0 (August 14, 2019)
#### Solid React Application Base
diff --git a/README.md b/README.md
index e4e341c7..c2de3372 100644
--- a/README.md
+++ b/README.md
@@ -42,10 +42,11 @@ Once the generator is installed, you can create a new application with just a fe
1. In a console window, navigate to the desired parent folder of the new application.
2. Use the command: ``` yo @inrupt/solid-react ```
-3. You will be prompted to set:
+3. You will be prompted:
1. An application name. This will also be the name of the new folder in which the new application lives.
- 2. A version number.
- 3. Whether the application is private or public.
+ 2. Whether or not to install a sample application, or a simple one-page application skeleton
+ 3. A version number.
+ 4. Whether the application is private or public.
4. Navigate into the new folder.
5. If you would like to start the application, simply run ``` npm run start ``` in the new folder, otherwise you can begin editing and writing your application!
diff --git a/__tests__/app.js b/__tests__/app.js
index 8c3f0b54..8497cabc 100644
--- a/__tests__/app.js
+++ b/__tests__/app.js
@@ -4,13 +4,13 @@ const assert = require('yeoman-assert');
const helpers = require('yeoman-test');
describe('generator-solid-react:app', () => {
- beforeAll(() => {
- return helpers
- .run(path.join(__dirname, '../generators/app'))
- .withPrompts({ someAnswer: true });
- });
+ beforeAll(() => {
+ return helpers
+ .run(path.join(__dirname, '../generators/app'))
+ .withPrompts({ someAnswer: true });
+ });
- it('creates files', () => {
- assert.file(['dummyfile.txt']);
- });
+ it('creates files', () => {
+ assert.file(['dummyfile.txt']);
+ });
});
diff --git a/generators/app/index.js b/generators/app/index.js
index b82c4dbb..24597df1 100644
--- a/generators/app/index.js
+++ b/generators/app/index.js
@@ -1,113 +1,204 @@
-const Generator = require("yeoman-generator");
-const chalk = require("chalk");
-const yosay = require("yosay");
-const figlet = require("figlet");
-const path = require("path");
-const mkdirp = require("mkdirp");
-const glob = require("glob");
-const voca = require("voca");
+const Generator = require('yeoman-generator');
+const chalk = require('chalk');
+const figlet = require('figlet');
+const path = require('path');
+const mkdirp = require('mkdirp');
+const voca = require('voca');
+
+const fileList = [
+ // ROOT FILES
+ { src: 'README.md' },
+ { src: 'package.json' },
+ // SRC ROOT FILES
+ { src: 'src/App.js' },
+ { src: 'src/App.styled.js' },
+ { src: 'src/App.test.js' },
+ { src: 'src/i18n.js' },
+ { src: 'src/index.css' },
+ { src: 'src/index.js' },
+ { src: 'src/serviceWorker.js' },
+ // CONFIG FILES
+ { src: 'config/**', dest: 'config' },
+ { src: 'scripts/**', dest: 'scripts' },
+ // PUBLIC FILES
+ { src: 'public/**', dest: 'public' },
+ // TEST FILES
+ { src: 'test/**', dest: 'test' },
+ // COMPONENTS
+ { src: 'src/components/**', dest: 'src/components' },
+ // CONSTANTS
+ { src: 'src/constants/**', dest: 'src/constants' },
+ // CONTEXTS
+ { src: 'src/contexts/**', dest: 'src/contexts' },
+ // HIGHER ORDER COMPONENTS
+ { src: 'src/hocs/**', dest: 'src/hocs' },
+ // HOOKS
+ { src: 'src/hooks/**', dest: 'src/hooks' },
+ // LAYOUTS
+ { src: 'src/layouts/**', dest: 'src/layouts' },
+ // SERVICES
+ { src: 'src/services/**', dest: 'src/services' },
+ // UTILS
+ { src: 'src/utils/**', dest: 'src/utils' },
+ // DEFAULT CONTAINERS
+ { src: 'src/containers/Login/**', dest: 'src/containers/Login' },
+ {
+ src: 'src/containers/PageNotFound/**',
+ dest: 'src/containers/PageNotFound',
+ },
+ { src: 'src/containers/Register/**', dest: 'src/containers/Register' },
+ { src: 'src/containers/Welcome/**', dest: 'src/containers/Welcome' },
+];
module.exports = class extends Generator {
- constructor(args, opts) {
- super(args, opts);
- }
+ prompting() {
+ const done = this.async();
+ this.log(chalk.cyan.bold('Welcome to the \n Solid React Generator'));
+
+ return this.prompt([
+ {
+ type: 'input',
+ name: 'appName',
+ message: 'Please enter your application name :',
+ store: true,
+ validate: appName => {
+ const pass = appName.match(/^[^\d\s!@£$%^&*()+=]+$/);
+ if (pass) {
+ return true;
+ }
+ return `${chalk.red(
+ 'Provide a valid "App name", digits and whitespaces not allowed'
+ )}`;
+ },
+ default: voca.kebabCase(this.appname), // Default to current folder name
+ },
+ {
+ type: 'confirm',
+ name: 'appInstalled',
+ message:
+ 'Solid React Generator can install an example application illustrating how to interact with Solid, or a basic application framework. Do you want to install the example application?',
+ },
+ {
+ type: 'input',
+ name: 'appVersion',
+ message: 'Initial version:',
+ store: true,
+ validate: appVersion => {
+ const pass = appVersion.match(
+ /^\d{1,2}\.\d{1,2}\.\d{1,2}$/
+ );
+ if (pass) {
+ return true;
+ }
+ return `${chalk.red(
+ 'Provide a valid version (ex: 0.1.0)'
+ )}`;
+ },
+ default: '0.1.0',
+ },
+ {
+ type: 'list',
+ name: 'isPrivate',
+ message: 'Is this application private?',
+ choices: ['false', 'true'],
+ default: 'false',
+ },
+ ]).then(answers => {
+ this.props = answers;
+ done();
+ });
+ }
- prompting() {
- const done = this.async();
- this.log(chalk.cyan.bold("Welcome to the \n Solid React Generator"));
+ writing() {
+ const { appName, appVersion, isPrivate, appInstalled } = this.props;
+ const pkgJson = {
+ name: appName,
+ version: appVersion,
+ private: isPrivate === 'true',
+ };
- return this.prompt([
- {
- type: "input",
- name: "appName",
- message: "Please enter your application name :",
- store: true,
- validate: appName => {
- const pass = appName.match(/^[^\d\s!@£$%^&*()+=]+$/);
- if (pass) {
- return true;
- }
- return `${chalk.red(
- 'Provide a valid "App name", digits and whitespaces not allowed'
- )}`;
- },
- default: voca.kebabCase(this.appname) // Default to current folder name
- },
- {
- type: "input",
- name: "appVersion",
- message: "version:",
- store: true,
- validate: appVersion => {
- const pass = appVersion.match(/^\d{1,2}\.\d{1,2}\.\d{1,2}$/);
- if (pass) {
- return true;
- }
- return `${chalk.red("Provide a valid version (ex: 0.1.0)")}`;
- },
- default: "0.1.0"
- },
- {
- type: "list",
- name: "isPrivate",
- message: "Is it private?",
- choices: ["false", "true"],
- default: "false"
- }
- ]).then(answers => {
- this.props = answers;
- done();
- });
- }
+ // FINALIZE FILES TO INSTALL
+ this.log('Processing Configuration...');
+ if (appInstalled) {
+ fileList.push(
+ { src: 'src/containers/index.js' },
+ { src: 'src/routes.js' },
+ {
+ src: 'src/constants/navigation.js',
+ dest: 'src/constants/navigation.js',
+ },
+ {
+ src: 'src/containers/Profile/**',
+ dest: 'src/containers/Profile',
+ },
+ {
+ src: 'src/containers/TicTacToe/**',
+ dest: 'src/containers/TicTacToe',
+ },
+ { src: '.env' }
+ );
+ } else {
+ fileList.push(
+ { src: 'src/_routes.lite.js', dest: 'src/routes.js' },
+ {
+ src: 'src/containers/_index.lite.js',
+ dest: 'src/containers/index.js',
+ },
+ {
+ src: 'src/constants/_navigation.lite.js',
+ dest: 'src/constants/navigation.js',
+ },
+ { src: '.env.lite', dest: '.env' },
+ {
+ src: 'src/components/AuthNavBar/_auth-nav-bar.lite.js',
+ dest: 'src/components/AuthNavBar/auth-nav-bar.component.js',
+ }
+ );
+ }
- writing() {
- const { appName, appVersion, isPrivate } = this.props;
- const pkgJson = {
- name: appName,
- version: appVersion,
- private: isPrivate === "true"
- };
+ this.log(chalk.blue(this.templatePath('package.json')));
- this.log(chalk.blue(this.templatePath("package.json")));
- const fromTemplateFiles = glob.sync(this.templatePath("./*"), {
- ignore: ["**/node_modules", "**/dist"],
- dot: true
- });
-
- if (path.basename(this.destinationPath()) !== appName) {
- this.log("Creating folder...");
- mkdirp(appName);
- this.destinationRoot(this.destinationPath(appName));
- }
+ if (path.basename(this.destinationPath()) !== appName) {
+ this.log('Creating folder...');
+ mkdirp(appName);
+ this.destinationRoot(this.destinationPath(appName));
+ }
- this.log("Copying app directory...");
+ this.log('Copying app directory...');
- this.fs.copyTpl(fromTemplateFiles, this.destinationRoot(), {
- title: voca.titleCase(appName)
- });
- this.fs.extendJSON(this.destinationPath("package.json"), pkgJson);
+ // EXTEND PACKAGE.JSON WITH USER PROMPTS
+ this.fs.extendJSON(this.destinationPath('package.json'), pkgJson);
- }
+ this.log(this.templatePath());
- install() {
- this.log("Installing dependencies...");
- this.npmInstall();
- this.completed = true;
- }
+ // WRITE NEW FILES BASED ON USER PROMPTS
+ fileList.forEach(newFile => {
+ return this.fs.copyTpl(
+ this.templatePath(newFile.src),
+ this.destinationPath(newFile.dest || newFile.src),
+ { title: voca.titleCase(appName) }
+ );
+ });
+ }
+
+ install() {
+ this.log('Installing dependencies...');
+ this.npmInstall();
+ this.completed = true;
+ }
- end() {
- if (this.completed) {
- this.log("Installation complete. Welcome to Solid");
- this.log(
- chalk.bold.blue(
- figlet.textSync("SOLID", {
- font: "3D-ASCII",
- horizontalLayout: "full",
- verticalLayout: "full"
- })
- )
- );
- return;
+ end() {
+ if (this.completed) {
+ this.log('Installation complete. Welcome to Solid');
+ this.log(
+ chalk.bold.blue(
+ figlet.textSync('SOLID', {
+ font: '3D-ASCII',
+ horizontalLayout: 'full',
+ verticalLayout: 'full',
+ })
+ )
+ );
+ }
}
- }
};
diff --git a/generators/app/templates/.env.lite b/generators/app/templates/.env.lite
new file mode 100644
index 00000000..d3d7ed08
--- /dev/null
+++ b/generators/app/templates/.env.lite
@@ -0,0 +1,3 @@
+REACT_APP_VERSION=$npm_package_version
+REACT_APP_NAME=$npm_package_name
+REACT_APP_COMPANY_NAME=inrupt Inc.
diff --git a/generators/app/templates/src/_routes.lite.js b/generators/app/templates/src/_routes.lite.js
new file mode 100644
index 00000000..2ad74f67
--- /dev/null
+++ b/generators/app/templates/src/_routes.lite.js
@@ -0,0 +1,31 @@
+import React, { Fragment } from 'react';
+import { PrivateLayout, PublicLayout, NotLoggedInLayout } from '@layouts';
+import { BrowserRouter as Router, Switch, Redirect } from 'react-router-dom';
+
+import { Login, Register, PageNotFound, Welcome, RegistrationSuccess } from './containers';
+
+const privateRoutes = [
+ {
+ id: 'welcome',
+ path: '/welcome',
+ component: Welcome
+ }
+];
+
+const Routes = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
+export default Routes;
diff --git a/generators/app/templates/src/components/AuthNavBar/_auth-nav-bar.lite.js b/generators/app/templates/src/components/AuthNavBar/_auth-nav-bar.lite.js
new file mode 100644
index 00000000..7bd9935a
--- /dev/null
+++ b/generators/app/templates/src/components/AuthNavBar/_auth-nav-bar.lite.js
@@ -0,0 +1,82 @@
+import React, { useState, useEffect, useCallback } from 'react';
+import { NavBar, Notification } from '@components';
+import { useTranslation } from 'react-i18next';
+import { NavBarContainer } from './children';
+import { LanguageDropdown } from '@util-components';
+import { ldflexHelper, errorToaster } from '@utils';
+import { NavigationItems } from '@constants';
+
+type Props = {
+ webId: string
+};
+
+const AuthNavBar = React.memo((props: Props) => {
+ const [inboxes, setInbox] = useState([]);
+ const { t, i18n } = useTranslation();
+ const navigation = NavigationItems.map(item => ({ ...item, label: t(item.label) }));
+ const { webId } = props;
+ /**
+ * Looks for all of the inbox containers in the pod and sets inboxes state
+ */
+ const discoverInbox = useCallback(async () => {
+ try {
+ let inboxes = [];
+ /**
+ * Get user's global inbox path from pod.
+ */
+ const globalInbox = await ldflexHelper.discoverInbox(webId);
+
+ if (globalInbox) {
+ inboxes = [
+ ...inboxes,
+ { path: globalInbox, inboxName: t('navBar.notifications.global'), shape: 'default' }
+ ];
+ }
+ /**
+ * If user doesn't has inbox in his pod will show an error and link to
+ * know how fix it.
+ */
+ if (inboxes.length === 0)
+ errorToaster(t('noInboxUser.message'), 'Error', {
+ label: t('noInboxUser.link.label'),
+ href: t('noInboxUser.link.href')
+ });
+ setInbox(inboxes);
+ } catch (error) {
+ /**
+ * Show general errors
+ */
+ errorToaster(error.message, t('navBar.notifications.fetchingError'));
+ }
+ }, [webId, inboxes]);
+
+ useEffect(() => {
+ if (webId) {
+ discoverInbox();
+ }
+ }, [webId]);
+ const { history } = props;
+
+ return (
+ ,
+ id: 'language'
+ },
+ {
+ component: () => ,
+ id: 'notifications'
+ },
+ {
+ component: props => ,
+ id: 'profile'
+ }
+ ]}
+ />
+ );
+});
+
+export default AuthNavBar;
diff --git a/generators/app/templates/src/components/AuthNavBar/auth-nav-bar.component.js b/generators/app/templates/src/components/AuthNavBar/auth-nav-bar.component.js
index 34ce5c7b..133db5cf 100644
--- a/generators/app/templates/src/components/AuthNavBar/auth-nav-bar.component.js
+++ b/generators/app/templates/src/components/AuthNavBar/auth-nav-bar.component.js
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
import { NavBarContainer } from './children';
import { LanguageDropdown } from '@util-components';
import { ldflexHelper, errorToaster, storageHelper } from '@utils';
+import { NavigationItems } from '@constants';
type Props = {
webId: string
@@ -12,26 +13,8 @@ type Props = {
const AuthNavBar = React.memo((props: Props) => {
const [inboxes, setInbox] = useState([]);
const { t, i18n } = useTranslation();
- const navigation = [
- {
- id: 'welcome',
- icon: '/img/icon/apps.svg',
- label: t('navBar.welcome'),
- to: '/welcome'
- },
- {
- id: 'profile',
- icon: '/img/people.svg',
- label: t('navBar.profile'),
- to: '/profile'
- },
- {
- id: 'tictactoe',
- icon: '/img/icon/tictactoe.svg',
- label: t('navBar.tictactoe'),
- to: '/tictactoe'
- }
- ];
+ const navigation = NavigationItems.map(item => ({ ...item, label: t(item.label) }));
+
const { webId } = props;
/**
* Looks for all of the inbox containers in the pod and sets inboxes state
diff --git a/generators/app/templates/src/components/AuthNavBar/children/NavbarProfile/nav-bar-profile.component.js b/generators/app/templates/src/components/AuthNavBar/children/NavbarProfile/nav-bar-profile.component.js
index 56563326..5ed1acf0 100644
--- a/generators/app/templates/src/components/AuthNavBar/children/NavbarProfile/nav-bar-profile.component.js
+++ b/generators/app/templates/src/components/AuthNavBar/children/NavbarProfile/nav-bar-profile.component.js
@@ -6,6 +6,7 @@ import { Dropdown } from '@util-components';
import auth from 'solid-auth-client';
import data from '@solid/query-ldflex';
import { errorToaster } from '@utils';
+import { ProfileOptions } from '@constants/navigation';
export const ImageContainer = styled.div`
width: 42px;
@@ -111,18 +112,11 @@ class NavBarProfile extends Component {
const { t, open, customClass } = this.props;
const { imageLoaded, image } = this.state;
- const profileOpts = [
- {
- label: t('navBar.profile'),
- onClick: this.profileRedirect,
- icon: 'cog'
- },
- {
- label: t('navBar.logOut'),
- onClick: this.logOut,
- icon: 'lock'
- }
- ];
+ const profileOpts = ProfileOptions.map(item => ({
+ ...item,
+ label: t(item.label),
+ onClick: this[item.onClick]
+ }));
return image ? (