Skip to content

Commit 817e117

Browse files
RitvikSardanaclaude
andcommitted
refactor: rename /internals to /experimental
- Rename internals.ts to experimental.ts with updated header comment - Rename docs/content/docs/internals.md to experimental.md with updated content - Update package.json exports from ./internals to ./experimental - Update sidebar link from Internals to Experimental - Rewrite P14 in PHILOSOPHY.md as 'Experimental carries no promise' - Added promotion criteria: generic (no backend deps), used in 2/3 apps, stable API Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d0ff02a commit 817e117

5 files changed

Lines changed: 34 additions & 24 deletions

File tree

PHILOSOPHY.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -452,14 +452,23 @@ const isDismissible = computed(() => {
452452
})
453453
```
454454

455-
### P14. Internals carry no promise
455+
### P14. Experimental carries no promise
456456

457-
**Rule:** Code reached through the `frappe-ui/internals` subpath is private and **exempt from P13**. It can change shape or be removed in any release — including minor/patch — with no deprecation window. Use it from first-party Frappe libraries without expecting stability.
457+
**Rule:** Code reached through the `frappe-ui/experimental` subpath is private and **exempt from P13**. It can change shape or be removed in any release — including minor/patch — with no deprecation window. Use it from first-party Frappe libraries without expecting stability.
458458

459-
**Why:** First-party libraries need to reuse internal building blocks, composables like `useInputLabeling`, class helpers, headless logic — without every one being promoted to the public API and frozen under P13. `internals` is that escape hatch: the framework gets to consume internals while the *public* surface stays small and the cost of evolving internals stays zero. The alternative, re-exporting each helper from the public API, or opening a `./src/*` wildcard either freezes everything under P13 or exposes everything forever. `internals` is the deliberate middle: a small, curated, explicitly-unstable surface.
459+
**What lives here:** two kinds of export. (1) *Internal building blocks* — composables, class helpers, and headless logic that frappe-ui's own components share (`useInputLabeling`, `inputFontSizeClasses`, the input-labeling components). (2) *New components* whose shape is still being discovered in real apps. Both are unstable on purpose: an internal because freezing it would freeze the components built on it, a new component because its API hasn't earned a stable contract yet.
460+
461+
**Why:** First-party libraries need to reuse these without every one being promoted to the public API and frozen under P13. `experimental` is that escape hatch: the framework gets to consume and iterate on building blocks and new components while the *public* surface stays small and the cost of evolving them stays zero. The alternative — re-exporting each helper from the public API, or opening a `./src/*` wildcard — either freezes everything under P13 or exposes everything forever. `experimental` is the deliberate middle: a small, curated, explicitly-unstable surface.
460462

461463
**Mechanics:**
462-
1. Exposed through a single curated barrel (`internals.ts`) behind the `./internals` export **not** a `./src/*` wildcard. Re-export only what a first-party consumer actually needs.
464+
1. Exposed through a single curated barrel (`experimental.ts`) behind the `./experimental` export **not** a `./src/*` wildcard. Re-export only what a first-party consumer actually needs.
463465
2. The barrel header restates the no-promise contract at the point of use.
464-
3. "Private" is by convention, `exports` can't scope visibility to a specific consumer so the contract is the disclaimer, not enforcement. Product/third-party code is told not to import it.
465-
4. To make an internal stable, deliberately promote it to a public entry point (and thus under P13). Until then, no guarantees.
466+
3. "Private" is by convention, `exports` can't scope visibility to a specific consumer so the contract is the disclaimer, not enforcement. Product/third-party code is told not to import it; if a product needs one of these and wants stability, it copies the code into its own app rather than depending on this subpath.
467+
4. To make an experimental export stable, deliberately promote it to a public entry point (and thus under P13). Until then, no guarantees.
468+
469+
**Promotion — moving a component out of `experimental` into the core public API.** A component graduates only when **all** of these hold:
470+
1. **Generic, not backend-dependent.** It solves a UI problem in the abstract — no coupling to a specific DocType, API shape, or app-specific data model. If it only makes sense inside one product, it stays in that product.
471+
2. **Proven across apps.** It is actually used in **two or three** of the Frappe apps (Helpdesk, CRM, Gameplan, Drive, …). Real reuse — not anticipated reuse — is the evidence that the abstraction holds.
472+
3. **Stable API.** Its props, events, slots, and behavior have settled; you no longer expect to reshape them. Promotion is the moment the API freezes under P13, so it must be ready to carry that promise.
473+
474+
Until a component clears all three, it lives in `experimental` and any consumer that needs guarantees keeps its own copy.

docs/components/Docs/sidebarList.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export function getSidebarList(
9191
{ text: 'Icons', link: '/docs/other/icons' },
9292
{ text: 'Utilities', link: '/docs/other/utilities' },
9393
{ text: 'Directives', link: '/docs/other/directives' },
94-
{ text: 'Internals', link: '/docs/internals' },
94+
{ text: 'Experimental', link: '/docs/experimental' },
9595
],
9696
},
9797
]
Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
# Internals
1+
# Experimental
22

3-
The `frappe-ui/internals` subpath exposes internal building blocks
4-
composables, class helpers, components and headless logic — that are not part of the
5-
public API.
3+
The `frappe-ui/experimental` subpath exposes internal building blocks and
4+
new, in-progress components — composables, class helpers, components and
5+
headless logic — that are not yet part of the stable public API.
66

77
Think of it as a staging area: exports live here while their API settles. Over
88
time, some of them get promoted to the public API and others get removed.
99

10-
> **Unstable API** — everything exported from `frappe-ui/internals` is exempt
11-
> from the usual deprecation policy and can change shape or disappear in _any_
12-
> release, including minor and patch releases, with no deprecation window.
13-
> Do **not** import this subpath from product apps or third-party code — pin
14-
> to a public entry point instead.
10+
> **Unstable API** — everything exported from `frappe-ui/experimental` is
11+
> exempt from the usual deprecation policy and can change shape or disappear in
12+
> _any_ release, including minor and patch releases, with no deprecation
13+
> window. If you depend on one of these from product or third-party code and
14+
> need stability, copy it into your own app rather than pinning to this
15+
> subpath.
1516
1617
## useInputLabeling
1718

@@ -22,7 +23,7 @@ internally, so a custom control built with it gets the same behavior and
2223
styling hooks for free.
2324

2425
```ts
25-
import { useInputLabeling } from 'frappe-ui/internals'
26+
import { useInputLabeling } from 'frappe-ui/experimental'
2627

2728
const { inputId, labelledBy, describedBy, hasError, errorLines, dataAttrs } =
2829
useInputLabeling(props, { size: () => props.size })
@@ -43,7 +44,7 @@ import {
4344
InputError,
4445
LabelingWrapper,
4546
useInputLabeling,
46-
} from 'frappe-ui/internals'
47+
} from 'frappe-ui/experimental'
4748
4849
const {
4950
inputId,
@@ -109,7 +110,7 @@ given size token (`'sm' | 'md' | 'lg' | 'xl'`), so custom controls render text
109110
at the same scale as built-in ones.
110111

111112
```ts
112-
import { inputFontSizeClasses } from 'frappe-ui/internals'
113+
import { inputFontSizeClasses } from 'frappe-ui/experimental'
113114

114115
inputFontSizeClasses('sm') // 'text-base'
115116
inputFontSizeClasses('lg') // 'text-lg'

internals.ts renamed to experimental.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// frappe-ui internals — UNSTABLE. No backward-compatibility promise.
1+
// frappe-ui experimental — UNSTABLE. No backward-compatibility promise.
22

33
export { inputFontSizeClasses } from './src/components/Combobox/utils'
44
export {

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@
4545
"import": "./src/index.ts",
4646
"types": "./src/index.ts"
4747
},
48-
"./internals": {
49-
"import": "./internals.ts",
50-
"types": "./internals.ts"
48+
"./experimental": {
49+
"import": "./experimental.ts",
50+
"types": "./experimental.ts"
5151
},
5252
"./frappe": {
5353
"import": "./frappe/index.js"
@@ -98,7 +98,7 @@
9898
"vite",
9999
"icons",
100100
"tailwind",
101-
"internals.ts",
101+
"experimental.ts",
102102
"tsconfig.base.json"
103103
],
104104
"repository": {

0 commit comments

Comments
 (0)