Open OCDkirby opened 5 months ago
we can re-export types and publish them, seems reasonable
we can re-export types and publish them, seems reasonable
Any implementation guidance? I can bang out some commits on a draft PR that will enable me to start on the reworked plugin CLI if you've got a direction in mind
if theres a way we can export as @types/quartz or something that would be awesome, im think we can just have a flat file that re-exports everything that might be important is probably ok? and then a github ci workflow to publish it
hosting as an npm package is a great idea. The syntax for their shorthand is @user/package
, and just package
works if it's unambiguous. @quartz
is taken, could we do @quartz-md/types
? I'd make that package have the Quartz repo as a URL dependency, then its index re-exports files from this repo. Will also set up a workflow.
EDIT: turns out we can publish to the @types
org. See DefinitelyTyped. I don't know if this type of package's code quality meets the standard for that repo because it's just plain hacky, we'd probably need a full refactor to publish there so making our own org is likely the best choice.
Working layout of the exports:
Plugin
: types from plugins/types
Component
: types related to components, such as those in components/types
Options
: configuration types used globally, at build time, and when developing pluginsUtil
: various useful types and functions from around the codebasei believe i still have https://www.npmjs.com/package/@jackyzha0/quartz-lib if we want to use that as well
(and yes, i was referring to definitelytyped haha, I've seen plenty of hacky libraries there so i don't see why not :))
as a side note, i think exporting cli arg types might also be good? idk
Sounds good. I've been making changes in https://github.com/OCDkirby/quartz-api and testing with @MaelImhof 's https://github.com/OCDkirby/quartz-pseudocode/tree/QuartzAPI, let me know how you want to proceed with moving stuff over. Should I clean it up and PR to jackyzha0/quartz-lib
on gh?
Edit: now also see https://github.com/OCDkirby/quartz-create-plugin , once hosted on npm at create-plugin
we'll be able to do npx @quartz-md/plugin myplugin
(or replace with whatever author hosts the package) to get a template going
@jackyzha0 looking at exporting the CLI types, but they're defined in JS as constants. Can I make a PR for some .d.ts
files for so I can re-export them from the API, or do you have a different idea in mind?
ah forgot about that, yeah lets do that another time then, not super urgent
we can keep it there for now but lets move it when its time for productionizing
I just realized, there's an interesting practical effect of the re-export route: A user's Quartz installation will depend on itself as a dependency of a dependency of a dependency if it uses community plugins (quartz depends on the plugin as a url, which depends on the api as an npm package, which depends on quartz as a url)
This just feels like bad design, especially if version mismatch occurs. Not sure if it's worth pursuing the re-export over extracting out the types into the quartz-lib
package.
One potential hack to keep my current approach: If we can force NPM dependency resolution of A depending on B and C where C<-D<-B to respect how A depends on B when resolving D, we can avoid version mismatch by having Quartz depend on itself as a filepath dependency (a hardlink in practice, at least on Linux systems)
Could we add it as a soft peer dependency? should solve the cycle problem
Perfect! PR incoming once I test that this works with a real plugin
I'm not sure I understand how this becomes cyclic, don't we have three libraries?
graph TD;
Q[Quartz itself]
T[Shared types]
P[Plugins]
Q --> T
Q --> P
P --> T
The types aren't shared, the way we decided to implement is just re-exporting. Nothing about base quartz changes (yet)
graph TD;
Q[Quartz itself]
T[Types re-exported]
P[Plugins]
Q --> P
P --> T
T --> Q
Anyway, quasi-peer deps and a self-dependency by Quartz solves the problem by making it look like
graph TD;
Q[Quartz itself]
Q2[Link to Quartz]
T[Types re-exported]
P[Plugins]
Q ~~~ Q2
Q --> P
Q --> Q2
T --> Q2
P --> T
I believe the two methods mentioned above are not commonly used practices in the Node community. The currently popular approach generally involves using workspaces in yarn, npm, or pnpm to build and install sub-packages. In other words, you can create a new npm package, store the types in the corresponding package, and then directly install this dependency in the top-level package.
-- package.json
-- workspace
-- package.json @quartz/types
I'll create a pull request later to demonstrate an example.
Here is a example:
https://github.com/iceprosurface/quartz/tree/refactor/type/packages/types
We can use tsup(Of course, you can also use tsc directly) to collect types and publish them to npm.
For example:
Input:
export * from './../../../quartz/util/path'
Output:
import { Element as Element$1 } from 'hast';
declare const clone: <T>(input: T) => T;
declare const QUARTZ = "quartz";
type SlugLike<T> = string & {
__brand: T;
};
/** Cannot be relative and must have a file extension. */
type FilePath = SlugLike<"filepath">;
declare function isFilePath(s: string): s is FilePath;
/** Cannot be relative and may not have leading or trailing slashes. It can have `index` as it's last segment. Use this wherever possible is it's the most 'general' interpretation of a slug. */
type FullSlug = SlugLike<"full">;
declare function isFullSlug(s: string): s is FullSlug;
/** Shouldn't be a relative path and shouldn't have `/index` as an ending or a file extension. It _can_ however have a trailing slash to indicate a folder path. */
type SimpleSlug = SlugLike<"simple">;
declare function isSimpleSlug(s: string): s is SimpleSlug;
/** Can be found on `href`s but can also be constructed for client-side navigation (e.g. search and graph) */
type RelativeURL = SlugLike<"relative">;
declare function isRelativeURL(s: string): s is RelativeURL;
declare function getFullSlug(window: Window): FullSlug;
declare function slugifyFilePath(fp: FilePath, excludeExt?: boolean): FullSlug;
declare function simplifySlug(fp: FullSlug): SimpleSlug;
declare function transformInternalLink(link: string): RelativeURL;
declare function normalizeRelativeURLs(el: Element | Document, destination: string | URL): void;
declare function normalizeHastElement(rawEl: Element$1, curBase: FullSlug, newBase: FullSlug): Element$1;
declare function pathToRoot(slug: FullSlug): RelativeURL;
declare function resolveRelative(current: FullSlug, target: FullSlug | SimpleSlug): RelativeURL;
declare function splitAnchor(link: string): [string, string];
declare function slugTag(tag: string): string;
declare function joinSegments(...args: string[]): string;
declare function getAllSegmentPrefixes(tags: string): string[];
interface TransformOptions {
strategy: "absolute" | "relative" | "shortest";
allSlugs: FullSlug[];
}
declare function transformLink(src: FullSlug, target: string, opts: TransformOptions): RelativeURL;
declare function endsWith(s: string, suffix: string): boolean;
declare function stripSlashes(s: string, onlyStripPrefix?: boolean): string;
export { type FilePath, type FullSlug, QUARTZ, type RelativeURL, type SimpleSlug, type TransformOptions, clone, endsWith, getAllSegmentPrefixes, getFullSlug, isFilePath, isFullSlug, isRelativeURL, isSimpleSlug, joinSegments, normalizeHastElement, normalizeRelativeURLs, pathToRoot, resolveRelative, simplifySlug, slugTag, slugifyFilePath, splitAnchor, stripSlashes, transformInternalLink, transformLink };
If the type system of Quartz is simple enough and doesn't have deep dependencies, then the exported types generated by tsup will be straightforward like the ones above. I believe this method is simple and effective. Moreover, if we plan to implement some built-in plugins in the future, using workspaces will definitely be a better solution.
This meta-issue is to request and track implementation of exposing the Quartz type system in a convenient way for developers. Specifically, plugin developers need access to types for a proper development and testing experience.
Specific goals relevant to community development:
Implementation suggestions:
quartz/types.ts
file that re-exports relevant types, where rest of the generator remains the sameRelated:
804
1085