Hyperapp: Standard folder structure/code organisation guidelines for hyper apps

Created on 14 Jun 2018  Β·  8Comments  Β·  Source: jorgebucaran/hyperapp

Now that Hyperapp has been out for a while and used by many people, I thought I'd open this issue so that we can all share how we organise our hyper apps.
Maybe we can come up with some sort of standard/supported folder structure together?

Some people have started working on some CLI to setup HA projects, these could potentially benefit from what is discussed in this issue.

PS: I know that HA 2.0 is right around the corner, but I think it is still very valuable to discuss this now because:

  • many people will still use HA 1 for some time
  • from the look of it, the standard app structure for HA 2.0 will should be too different from the standard for HA 1

Looking forward to the discussions down below.

Discussion

Most helpful comment

I’m not sure it’s a good idea to promote a β€œstandard and official” folder structure. Everyone does it differently, and as long as everyone does what works for them, why argue with each other who does it better?

But @Mytrill s original suggestion of sharing and explaining the ideas behind it is great. Should be very helpful to newcomers!

All 8 comments

For the record, here is the folder structure that I found works best for me (it's for typescript, but the only typescript specific files are the api.ts):
App folder structure:

dist/       # bundled files
src/        # sources
  module1/     # module folders (detailed below)
  module2/
  common/      # ... common stuff, sometimes I call this lib/
  components   # implementations for simple components (props => VNode)
    Component1.scss
    Component1.tsx
    Component2.scss
    Component2.tsx
    index.ts       # export * from "./Component1"
                   # export * from "./Component2"
  api.ts    # the typescript definitions for the State, Actions, and all relevant types
  state.ts  # the state object
  view.ts   # the view function
  index.ts  # calls app(state, actions, view, element)
  index.html
  # other top level files
package.json
# other files ...

Module folder structure:

submodule1/  # nested modules, same folder structure as this
submodule2/
actions/     # implementations for actions (usage: import * as actions from "./actions")
  action1.ts
  action2.ts
  index.ts      # export * from "./action1"
                # export * from "./action2"
                # import * as submodule1 from "../submodule1/actions"
                # export { submodule1 }
components/  # implementations for simple components (props => VNode)
  Component1.scss
  Component1.tsx
  Component2.scss
  Component2.tsx
  index.ts      # export * from "./Component1"
                # export * from "./Component2"
api.ts    # the typescript definitions for the State, Actions, and all relevant types
state.ts  # the state object
LazyComponent1.scss # these are the lazy components (props => (state, actions) => VNode)
LazyComponent1.tsx
LazyComponent2.scss
LazyComponent2.tsx
index.ts

I guess, for most Web based projects, the tree folder structure can be simplified to this (using JSX).

β”œβ”€β”€ public
β”‚Β Β  └── js
β”‚Β Β      └── bundle.js
└── src
    β”œβ”€β”€ components
    β”‚Β Β  β”œβ”€β”€ about.jsx <-- requires partials/**/*
    β”‚Β Β  β”œβ”€β”€ contact.jsx <-- requires partials/**/*
    β”‚Β Β  β”œβ”€β”€ home.jsx <-- requires partials/**/*
    β”‚Β Β  └── partials
    β”‚Β Β      β”œβ”€β”€ footer.jsx
    β”‚Β Β      β”œβ”€β”€ header.jsx
    β”‚Β Β      └── main.jsx
    β”œβ”€β”€ index.html
    β”œβ”€β”€ index.jsx <-- requires components/*.jsx & utils/**/*
    └── utils
        β”œβ”€β”€ actions.jsx
        └── state.jsx

In my opinion, it looks cleaner and is safer to scale for small to mid-sized project using Hyperapp 1.x.x. I'm currently working on a Rollup based template for Hyperapp and this is the folder structure I'm currently testing in development to see how convenient it is for all kind of project. Let me know what you think about it!

How I've done it with a couple projects now, and how I've implemented it in Hyperapp Kit, the structure is like so:

index.html contains the index.js script which mounts the app. The state, actions and view live in their own file. Each component gets its own JS and SCSS file separate with each component' styles being imported into index.scss. (I'd prefer them in the same file, but without CSS-in-JS this is an ok solution - main thing I dislike here is maintaining separate files for a component.)

img could be renamed to assets to house different types.

I feel that an agreed upon folder structure is a great idea. I acknowledge it will be impossible to fit all use cases. This said, I feel that the above example from @aminnairi is simple, understandable, and a good base to start from.

One thing I would strongly advise against is the uppercase first letter present in the other 2 examples. It seems that this is being used to signify a component file. However these files can be identified in both folder structures as components due to residing in a "components" folder. This is unnecessary duplication of intent.

I also like consistency in the naming convention of all stuff in my application whether it is a component or an exported function. But this is more of a personnal concern than a guideline as it is not impacting the organization.

I guess sometimes, the bundler help in a way that, for instance, with Webpack's alias, we can make a 1-2 level deep folders' structure, and just call things like :

// /app/jsx/app.jsx
import 'SCSS/app.scss'

with a Webpack configured like so :

module.exports = {
  resolve: {
    alias: {
      SCSS: __dirname + '/app/scss/'
    }
  }
}

instead of doing :

// /app/jsx/app.jsx
import '../scss/app.scss'

But this might be just me hating double-dot when we can use aliases in Webpack (not sure how it is done with other bundlers though). It has the advantage to be cleaner and easier to understand for newcomers inspecting the source code as every JSX will be in the same directory, every SCSS either etc... But I can see a problem when working in a large-scale application. There might be a perfect spot between aliasing low-level folders and subfolders by theme organization.

@frenzzy @zaceno @lukejacksonn Do you have any opinion on this? Really just curious. πŸ˜„

I’m not sure it’s a good idea to promote a β€œstandard and official” folder structure. Everyone does it differently, and as long as everyone does what works for them, why argue with each other who does it better?

But @Mytrill s original suggestion of sharing and explaining the ideas behind it is great. Should be very helpful to newcomers!

Here was a starting point to standardize a directory layout: Folder Structure Conventions

A folder structure mainly depends on the project type and may vary project to project. Only the base (always used files) could be at the same place.

For example the base directory layout of Hyperapp Starter:

.
β”œβ”€β”€ build/                         # Compiled output
β”œβ”€β”€ node_modules/                  # 3rd-party libraries and utilities
β”œβ”€β”€ public/                        # Static files such as favicon.ico etc.
β”œβ”€β”€ src/                           # Application source code
β”‚   β”œβ”€β”€ app.js                     # Universal (Isomorphic) application entry point
β”‚   β”œβ”€β”€ index.js                   # Client-side rendering, e.g. app(state, actions, view, container)
β”‚   └── server.js                  # Server-side rendering, e.g. renderToString(view, state, actions)
β”œβ”€β”€ .env                           # Environment variables
β”œβ”€β”€ package.json                   # The list of project dependencies + NPM scripts
└── README.md                      # Getting started guide

The advanced directory layout of a huge project may look like this:

β”œβ”€β”€ .circleci/                     # Continuous integration configuration files
β”œβ”€β”€ .vscode/                       # Project snippets and run/debug configuration for Text Editor/IDE
β”œβ”€β”€ benchmarks/                    # Load and stress tests
β”œβ”€β”€ build/                         # Compiled output
β”œβ”€β”€ configs/                       # Configuration overrides for Babel, Bundler, etc. Example: https://github.com/frenzzy/hyperapp-tools/tree/master/configs
β”œβ”€β”€ docs/                          # Documentation files
β”œβ”€β”€ migrations/                    # Database schema migration files
β”œβ”€β”€ node_modules/                  # 3rd-party libraries and utilities
β”œβ”€β”€ public/                        # Static files such as favicon.ico etc.
β”œβ”€β”€ scripts/                       # Automation scripts (yarn update-schema etc.)
β”œβ”€β”€ seeds/                         # Reference and seed data for the database
β”œβ”€β”€ src/                           # Application source code
β”‚   β”œβ”€β”€ common/                    # Shared Hyperapp/React components and HOCs
β”‚   β”‚   └── {component}/           # Any component name or reusable block
β”‚   β”‚       β”œβ”€β”€ {component}.js     # Or {component}.browser.js + {component}.node.js
β”‚   β”‚       β”œβ”€β”€ {component}.test.js
β”‚   β”‚       β”œβ”€β”€ {component}.css
β”‚   β”‚       β”œβ”€β”€ {component}.i18n.json
β”‚   β”‚       β”œβ”€β”€ {image}.{svg,png,jpg,gif}
β”‚   β”‚       β”œβ”€β”€ package.json       # Allows to use require('component') instead of require('component/component') and import different files for browser and server when necessary
β”‚   β”‚       └── README.md          # If necessary
β”‚   β”œβ”€β”€ fonts/                     # Custom web fonts
β”‚   β”œβ”€β”€ icons/                     # Reusable svg icons
β”‚   β”œβ”€β”€ pages/                     # Web app pages (/news, /store, /users, /users/:id, /admin, etc)
β”‚   β”‚   └── {page}/                # The same structure as for common/ but the files are not reusable and the whole folder could be deleted safely
β”‚   β”œβ”€β”€ server/                    # Server-side code (API, authentication, etc.)
β”‚   β”œβ”€β”€ app.js                     # Universal (Isomorphic) application entry point
β”‚   β”œβ”€β”€ app.test.js                # Unit tests for app.js
β”‚   β”œβ”€β”€ app.css                    # Global styles (for html, body, h1 etc.)
β”‚   β”œβ”€β”€ index.js                   # Client-side entry point
β”‚   β”œβ”€β”€ server.js                  # Server-side entry point
β”‚   β”œβ”€β”€ serviceWorker.js           # Service worker entry point
β”‚   └── theme.js                   # UI theme variables (for example overrides for Material UI default styles)
β”œβ”€β”€ ssl/                           # SSL certificates (for example for connecting to Cloud SQL instance)
β”œβ”€β”€ tests/                         # End-to-end, integration tests
β”œβ”€β”€ .env                           # Environment variables
β”œβ”€β”€ .env.{environment}             # Environment variables overrides for a specific environment
β”œβ”€β”€ docker-compose.yml             # Defines Docker services, networks and volumes
β”œβ”€β”€ docker-compose.override.yml    # Overrides per developer environment (not under source control)
β”œβ”€β”€ Dockerfile                     # Commands for building a Docker image for production
β”œβ”€β”€ graphql.schema                 # GraphQL schema (auto-generated, used by Apollo or Relay)
β”œβ”€β”€ package.json                   # The list of project dependencies + NPM scripts
└── README.md                      # Getting started guide
Was this page helpful?
0 / 5 - 0 ratings

Related issues

jbrodriguez picture jbrodriguez  Β·  4Comments

zaceno picture zaceno  Β·  3Comments

ghost picture ghost  Β·  3Comments

guy-kdm picture guy-kdm  Β·  4Comments

dmitrykurmanov picture dmitrykurmanov  Β·  4Comments