New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(theme-common): split package into public/internal API entrypoints #7660
Conversation
✅ [V2]
To edit notification comments on pull requests, go to your Netlify site settings. |
⚡️ Lighthouse report for the deploy preview of this PR
|
Size Change: -4 B (0%) Total Size: 802 kB ℹ️ View Unchanged
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My idea is we don't have to be too conservative about marking APIs as public. General utilities should definitely be public. I feel like we should have more public APIs than internal ones. I marked those that I have the strongest opinions about, but we need to think about every one of them, probably make a ranking.
Some other thoughts:
- We definitely can select certain "interesting" utilities to document while only keeping others retro-compatible (i.e. safe for eject) without documentation
- "Public" can mean three things:
- External theme authors are encouraged to use these
- Site creators are encouraged to use these in their custom pages
- Safe for swizzle eject
export { | ||
useFilteredAndTreeifiedTOC, | ||
useTreeifiedTOC, | ||
type TOCTreeNode, | ||
} from './utils/tocUtils'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least useTreeifiedTOC
is very useful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useTreeifiedTOC
is not used in any theme component, only useFilteredAndTreeifiedTOC
is, which does feel like a weird name that we may want to change currently. Similarly, not sure i's useful to publicly commit to retro-compatibility on apis that even our own theme does not use. In the end we want the TOC components to be safe to swizzle, and in this case it doesn't help.
I would be ok to expose a unique useTOCTree(array, options)
or something later, but this requires some refactoring.
I want to keep this PR clean from refactors: focusing only on splitting private/internal that we are ready to expose right now, as is.
What I mean is that we can do extra refactors in subsequent PRs, and it's not a big deal if this one API is not public immediately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I once planned to document useTreeifiedTOC
in #6729, because it's no longer possible to pick arbitrary TOC subtrees so users need to implement this themselves. But I agree the API is not perfect yet and can benefit from some more refactors.
Agree on that.
Looks similar to what I had in mind, except I grouped the first 2 cases as "documented APIs" (~= encouraged usage) Note this PR somehow split APIs to document from APIs to not document. We may not 100% agree on this split yet, but it's not a big deal as those are not documented yet 🤪 We'll refine that later on a case-by-case basis, for now I just wanted to have an initial set of public APIs extracted |
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
import fs from 'fs-extra'; | ||
|
||
// See comment in src/internal.ts | ||
// This script should be run by CI tests to remove: | ||
// export * from './index' | ||
|
||
const filePath = 'lib/internal.js'; | ||
const lineToRemove = "export * from './index';\n"; | ||
|
||
if (!(await fs.pathExists(filePath))) { | ||
throw new Error(`internal entrypoint file not found at ${filePath}`); | ||
} | ||
|
||
const fileContent = await fs.readFile(filePath, 'utf8'); | ||
|
||
const fileContentUpdated = fileContent.replaceAll(lineToRemove, ''); | ||
|
||
// Ensure the script correctly removes the re-export | ||
if (fileContent === fileContentUpdated) { | ||
throw new Error( | ||
'Unexpected: internal re-export has not been replaced.\nMake sure this script works, and is only run once.', | ||
); | ||
} | ||
|
||
await fs.writeFile(filePath, fileContentUpdated); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lol, this looks like pure hack... Since we are to automate the marking of ejection safety by analyzing dependency on /internal
APIs, we can simply reuse the same analyzer. I'll investigate that later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes 😅 maybe we'll remove this in the future once we have more public APIs
But this is not exactly the same problem: If a component is unsafe, it should still preferably import from the public endpoint instead of the internal re-export.
any concern to merge this? We can move internal -> public and document things in subsequent PRs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost good—last concern.
type NavbarSecondaryMenuComponent, | ||
} from './contexts/navbarSecondaryMenu/content'; | ||
export {useNavbarSecondaryMenu} from './contexts/navbarSecondaryMenu/display'; | ||
export {duplicates, uniq} from './utils/jsUtils'; | ||
|
||
export {useBackToTopButton} from './hooks/useBackToTopButton'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this really stable? We have a lot of "wiring hooks" like useAnnouncementBar
, useNavbarMobileSidebar
, etc., and this is the only public one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to internal for now as I'm not 100% satisfied with the api, but we'll have to figure out how to make all these public on a case-by-case basis to ensure we can safely swizzle the related components.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM—I think this is a good starting point and there's plenty to document already.
Breaking change
@docusaurus/theme-common
APIs have been moved to@docusaurus/theme-common/internal
: if your swizzled components stop working, try updating the importsPre-flight checklist
Motivation
We want to make it more explicit which
@docusaurus/theme-common
APIs are public, and which are internal.We'll maintain semver retrocompatibility only on public APIs.
Work in progress, details to be discussed.
IMHO there is 3 kind of APIs:
@docusaurus/theme-common
public APIs with guaranteed retrocompatibility@docusaurus/theme-common/internal
private APIs on which we can safely do undocumented breaking changesTest Plan
Preview keeps working
Test links
Deploy preview: https://deploy-preview-_____--docusaurus-2.netlify.app/
Related issues/PRs
v2.0 launch plan: #6113