Add design spec for JS module split
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
11767f147a
commit
d28f87a3c4
1 changed files with 116 additions and 0 deletions
116
docs/superpowers/specs/2026-05-08-js-module-split-design.md
Normal file
116
docs/superpowers/specs/2026-05-08-js-module-split-design.md
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
# JS Module Split Design
|
||||
|
||||
## Goal
|
||||
|
||||
Split `public/app.js` (1025 lines, single file) into ES modules under `public/js/` — no build step, no npm. Modernize `var` to `const`/`let` in the process.
|
||||
|
||||
## Module Structure
|
||||
|
||||
```
|
||||
public/js/
|
||||
state.js shared mutable state object
|
||||
i18n.js translations, locale logic
|
||||
api.js fetch wrapper, loadGoals(), saveGoal()
|
||||
goals.js pure goal calculations (calc, tOff, dcls, …)
|
||||
ui.js tpl(), showToast(), overlay helpers, updateHeader()
|
||||
auth.js login/register/password screens
|
||||
sheets.js openNew(), openData(), openAdmin()
|
||||
render.js buildCard(), render(), wire()
|
||||
app.js entry point: init, URL params, stopwatch, midnight timer
|
||||
```
|
||||
|
||||
## State Management
|
||||
|
||||
Single exported object in `state.js`. All modules import and mutate it directly:
|
||||
|
||||
```js
|
||||
// state.js
|
||||
export const state = {
|
||||
goals: [],
|
||||
userName: '',
|
||||
isAdmin: false,
|
||||
prefs: {},
|
||||
selDay: {},
|
||||
addAmt: {},
|
||||
renamingId: null,
|
||||
renameVal: '',
|
||||
collapsed: {},
|
||||
TODAY: new Date(),
|
||||
};
|
||||
```
|
||||
|
||||
Replaces all global `var` declarations at the top of `app.js`. Mutations like `goals = data` become `state.goals = data`.
|
||||
|
||||
## Dependency Graph
|
||||
|
||||
```
|
||||
state.js ← all modules
|
||||
i18n.js ← goals.js, ui.js, auth.js, sheets.js, render.js, app.js
|
||||
api.js ← auth.js, sheets.js, app.js
|
||||
goals.js ← render.js, sheets.js
|
||||
ui.js ← auth.js, sheets.js, render.js
|
||||
auth.js ← sheets.js, app.js
|
||||
render.js ← app.js
|
||||
```
|
||||
|
||||
## Circular Import Solution
|
||||
|
||||
`api.js` previously called `showLogin()` on 401 — creating a circular dependency with `auth.js`. Resolved via a Custom Event:
|
||||
|
||||
```js
|
||||
// api.js — on 401:
|
||||
document.dispatchEvent(new CustomEvent('session-expired'));
|
||||
|
||||
// app.js — on startup:
|
||||
document.addEventListener('session-expired', () => showLogin());
|
||||
```
|
||||
|
||||
Events are used only for this cross-cutting concern. All other communication is via direct imports.
|
||||
|
||||
## const/let Conversion
|
||||
|
||||
- ES modules run in strict mode automatically
|
||||
- `var` → `const` where the binding is never reassigned
|
||||
- `var` → `let` for loop counters and mutated locals
|
||||
- State re-assignments (`goals = newArray`) → `state.goals = newArray`
|
||||
- `TODAY` moves into `state.TODAY`, updated in `scheduleMidnight()` and `visibilitychange`
|
||||
|
||||
## HTML Change
|
||||
|
||||
`templates/app.html.twig`:
|
||||
|
||||
```html
|
||||
<!-- before -->
|
||||
<script src="{{ asset('app.js') }}"></script>
|
||||
|
||||
<!-- after -->
|
||||
<script type="module" src="{{ asset('js/app.js') }}"></script>
|
||||
```
|
||||
|
||||
`public/app.js` is deleted after the migration is complete.
|
||||
|
||||
## File Size Estimates
|
||||
|
||||
| File | Est. lines |
|
||||
|---|---|
|
||||
| state.js | ~15 |
|
||||
| i18n.js | ~185 |
|
||||
| api.js | ~35 |
|
||||
| goals.js | ~60 |
|
||||
| ui.js | ~35 |
|
||||
| auth.js | ~130 |
|
||||
| sheets.js | ~135 |
|
||||
| render.js | ~185 |
|
||||
| app.js | ~60 |
|
||||
| **Total** | **~840** |
|
||||
|
||||
(Reduction due to removed comments and tighter const/let style.)
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
One module at a time, keeping the app functional throughout:
|
||||
|
||||
1. Create `public/js/` directory
|
||||
2. Write modules in dependency order: state → i18n → api → goals → ui → auth → sheets → render → app
|
||||
3. Switch HTML to `<script type="module">` when all modules are ready
|
||||
4. Delete old `public/app.js`
|
||||
Loading…
Reference in a new issue