Skip to content

Conversation

@TomStrepsil
Copy link
Owner

@TomStrepsil TomStrepsil commented Aug 21, 2025

Issue

resolves ASOS#46

Details

As per ASOS#46 (comment) needs a cross-merge of ASOS#43 to get this working, and for ASOS#55 to be merged

Readme

Express "parallel folder convention" example

This example shows the use of the react-pointcuts, features, ssr and webpack packages, as part of an express application.

An inbound header named "feature" can take the following values:

  • baseline (or omitted)
  • feature1
  • feature2
  • feature3
  • feature4

...which is used server side (via a "node request scoped features store") to generate appropriate server-rendered content.

The chosen feature state is serialized to the browser using the ssr package, and loaded into a "global features store", using valtio browser-side, for reactivity.

To demonstrate the reactivity, a drop-down allows changing of the selected feature state.

Filesystem structure

The base / control folder structure is thus:

├── components
│   ├── Animal
│   │   └── index.tsx
│   ├── BottomBox
│   │   ├── index.tsx
│   │   └── useAnimals.ts
│   └── TopBox
│       ├── TopBoxChild
│       │   ├── TopBoxButton
│       │   │   ├── index.module.css
│       │   │   ├── index.tsx
│       │   │   └── useAddAnimal.ts
│       │   └── index.tsx
│       └── index.tsx
├── constants
│   └── index.ts
└── state
    ├── modules
    │   ├── animals
    │   │   └── slice.ts
    │   └── index.ts
    └── store.ts

Variations

The features comprise the following:

baseline

The base experience. Clicking the dog dispatches a redux action adding a dog to the bottom box

feature1

Varied react components (TopBox & BottomBox), at various depths in the folder structure. A varied constant for the animal emoji (constants/index.ts) and varied css (background colour of the button) (TopBoxButton/index.module.css).

├── components
│   ├── BottomBox
│   │   └── index.tsx
│   └── TopBox
│       ├── TopBoxChild
│       │   └── TopBoxButton
│       │       └── index.module.css
│       └── index.tsx
└── constants
    └── index.ts

feature2

Varied constant (constants/index.ts), react component (TopBoxChild) and redux slice (slice.ts) connecting an additional action creator (useFreeAnimal.ts) to "free" added animals (clears the state collection). Has an alternate "initial state" containing two hamsters, activated during server rendering by a feature header containing feature2.

├── components
│   └── TopBox
│       └── TopBoxChild
│           ├── index.tsx
│           └── useFreeAnimals.ts
└── constants
    └── index.ts

feature3

Varied constant (constants/index.ts) & redux slice (slice.ts) with modified reducer action that multiplies rabbits, when added

├── constants
│   └── index.ts
└── state
    └── modules
        └── animals
            └── slice.ts

feature4

Varied constant (constants/index.ts) & redux slice (slice.ts) with replaced redux selector that carcinizes previously added animals

├── constants
│   └── index.ts
└── state
    └── modules
        └── animals
            └── slice.ts

feature5

Varied component (TopBox) & redux store (modules/index.ts) that introduces a new "space" slice, with it's own state containing spacey stuff.

├── components
│   └── TopBox
│       ├── index.tsx
│       ├── styles.module.css
│       └── useSpaceStuff.ts
└── state
    └── modules
        ├── space
        │   └── slice.ts
        └── index.ts

Explanation

The webpack plugin is configured with a toggle handler that maps variants to controls based on a parallel root folder. This allows for any file to be replaced at any depth. The example shows both complete replacements, and augmentations (importing of the base, then modifying).

To vary react components, the toggle point from the react-pointcuts package is used.

To vary CSS files, constants, and redux "slices", a toggle point is used that utilises an object proxy, to intercept property access. Despite objects not being innately reactive, as long as they are accessed from something that does update with change of state (i.e. react components), their properties will also update based on the new feature.

To vary redux reducer map, a toggle point that wraps the factory method (getReducerMap) is used.

To ensure that the redux store is reactive to feature state, a StateProvider react component is used to host the react-redux provider, that subscribes to the valtio state, and calls replaceReducer from the @reduxjs/toolkit whenever the feature state changes. This ensures a new redux store (but retaining current state) is made available to the react components. Care should be taken to ensure the existing state is compatible with any updated selectors etc.

N.B. It is assumed that feature state will not change during a request cycle on the server, so valtio is only plumbed in to the client-side feature store, via conditional compilation using Webpack's DefinePlugin.

Screenshots

image

3x button click:
image

change to "feature 1":
image

2x button click:
image

change to "feature 2":
image

click "⛓️‍💥"
image

change to "feature 3":
image

4x button click:
image

change to "feature 4":
image

reload, with "feature2" header, then change to "feature 5":
image

CheckList

  • PR starts with [ISSUE_ID].
  • Has been tested (where required) before merge to main.

TomStrepsil and others added 21 commits December 24, 2024 17:31
* rename to proper module namespace

* update docs links

* update versions

* web toggle point in readme title

* fixup changelog from revised 0.x range

* 2.0.0 -> 0.5.0 in oss version scheme

* fix broken link syntax in CHANGELOG

* consistent quoting

* more version history issues

* fixup module name in jsdoc

* add web
remove sdkInstanceProvider

* remove SDKInstanceProvider

* fixup jsdoc dedupe

* tweak

* clarity re: ssr package

* casing etc
* update workflows

* version

* typo

* update chromium linux snaps

* versions for serve update

* package.json repository field

* update root package.lock

* bugs & directories/doc fields

* fix changelog

---------

Co-authored-by: Tom Pereira <[email protected]>
@TomStrepsil TomStrepsil changed the title parallel folder example [ASOS#46] parallel folder example Aug 22, 2025
@TomStrepsil TomStrepsil changed the base branch from feat/54-support-bidirectional-filesystem-conventions to main October 21, 2025 17:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

How to handle large, multi-directory changes

2 participants