FeaturesTemplatesShowcaseTownie
AI
BlogDocsPricing
Log inSign up
cricks_unmixed4u
cricks_unmixed4uai-prompted
Public
Like
ai-prompted
Home
Code
11
.cursor
1
backend
1
frontend
2
shared
.cursorrules
.vtignore
README.md
deno.json
knowledge.md
learning_goal.md
main.tsx
Branches
1
Pull requests
Remixes
10
History
Environment variables
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data โ€“ all from the browser, and deployed in milliseconds.
Sign up now
Code
/
learning_goal.md
Code
/
learning_goal.md
Search
6/18/2025
Viewing readonly version of main branch: v25
View latest version
learning_goal.md

Learning Project

The goal is to explore an idea.

Context

I have a task. I'm a senior developer. There's an app that uses a fork of an existing app, and the existing app is a full-stack app, not modularized. It's not intended to be customized, but the wrapper app is customizing it nevertheless. The challenge is that we would like to both keep up with upstream changes and be freely able to change the files we're working on. It's using Svelte, so this is a bit difficult. We surveyed our options.

Best option

Small permanent fork that injects formal extension points, then merge upstream as a git subtree. โ€ข You fork once to add an official PluginHost with typed hooks/events. โ€ข Upstreamโ€™s future commits are pulled into the subtree; conflicts surface only where the hook layer lives. โ€ข Your product repo contains only plug-ins, themes, and routes.

The hook contract is explicit & testableโ€”avoids โ€œalias drift.โ€ Subtree lets you track upstream SHA, not just a version string, so you can cherry-pick bug-fixes quickly. Requires initial negotiation (or brute-force) to land hooks upstream. Subtree UX in Git is less familiar than submodules; team needs a short how-to.

// upstream/src/lib/pluginHost.ts โ† (tiny, permanent fork) import { setContext, getContext } from 'svelte';

type ScriptHooks<T = any> = { beforeInit?: (state: T) => T; mounted?: (ctx: { state: T; update: (next: Partial) => void }) => void; updated?: (ctx: { state: T }) => void; };

const key = Symbol('plugin-bus');

export function initPluginBus() { const map = new Map<string, ScriptHooks[]>(); setContext(key, { register(component: string, hooks: ScriptHooks) { const list = map.get(component) ?? []; list.push(hooks); map.set(component, list); }, get(component: string) { return map.get(component) ?? []; } }); }

export function scriptHooksFor(component: string): ScriptHooks[] { return getContext<{ get: (c: string) => ScriptHooks[] }>(key).get(component); }

<button on:click={() => update({ count: state.count + 1 })}> Clicked {state.count} times

// product/plugins/widget-analytics.ts โ† lives outside the subtree import { scriptHooksFor } from 'upstream/lib/pluginHost'; // re-export in your barrel

// register at app start-up import '$lib/bootstrap/plugins'; // e.g. in main.ts

// product/src/bootstrap/plugins.ts import { initPluginBus } from 'upstream/lib/pluginHost'; const bus = initPluginBus();

bus.register('Widget', { beforeInit: s => ({ ...s, count: 42 }), // default to 42 mounted : ({ state }) => console.log('mount', state), updated : ({ state }) => sendMetric(state.count) // analytics });

How to explore the idea?

"Simulate" the upstream and downstream as folders. Upstream is import mapped so that it can be importent "as if a package @deepend". Downstream is imported locally.

Use Svelte.

Dependencies

@sveltejs/adapter-auto: 3.2.2 @sveltejs/adapter-static: ^3.0.2 @sveltejs/kit: ^2.5.20 @sveltejs/vite-plugin-svelte: ^3.1.1 @tailwindcss/container-queries: ^0.1.1 @tailwindcss/postcss: ^4.0.0 @tailwindcss/typography: ^0.5.13 @typescript-eslint/eslint-plugin: ^8.31.1 @typescript-eslint/parser: ^8.31.1 cypress: ^13.15.0 eslint: ^8.56.0 eslint-config-prettier: ^9.1.0 eslint-plugin-cypress: ^3.4.0 eslint-plugin-svelte: ^2.43.0 i18next-parser: ^9.0.1 postcss: ^8.4.31 prettier: ^3.3.3 prettier-plugin-svelte: ^3.2.6 sass-embedded: ^1.81.0 svelte: ^4.2.18 svelte-check: ^3.8.5 svelte-confetti: ^1.3.2 tailwindcss: ^4.0.0 tslib: ^2.4.1 typescript: ^5.5.4 vite: ^5.4.14 vitest: ^1.6.1

Go to top
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Product
FeaturesPricing
Developers
DocsStatusAPI ExamplesNPM Package Examples
Explore
ShowcaseTemplatesNewest ValsTrending ValsNewsletter
Company
AboutBlogCareersBrandhi@val.town
Terms of usePrivacy policyAbuse contact
ยฉ 2025 Val Town, Inc.