@d1vij/jassm 0.1.17 → 0.1.18-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -33,10 +33,10 @@ import react from "@vitejs/plugin-react";
33
33
  import jassm from "@d1vij/jassm/plugin";
34
34
 
35
35
  export default defineConfig({
36
- plugins: [
37
- jassm(), // Put jassm plugin before react's plugin
38
- react(),
39
- ],
36
+ plugins: [
37
+ jassm(), // Put jassm plugin before react's plugin
38
+ react(),
39
+ ],
40
40
  });
41
41
  ```
42
42
 
@@ -56,12 +56,12 @@ echo "# This is a Heading" > sample.mdx
56
56
  import { Registry } from "@d1vij/jassm";
57
57
 
58
58
  export const registry = new Registry({
59
- modules: import.meta.glob("/src/assets/mdx/**/*.mdx"),
60
- source: "/src/assets/mdx",
61
- mountOn: "/root",
62
- records: {
63
- "/sample": "/example.mdx",
64
- },
59
+ modules: import.meta.glob("/src/assets/mdx/**/*.mdx"),
60
+ source: "/src/assets/mdx",
61
+ mountOn: "/root",
62
+ records: {
63
+ "/sample": "/example.mdx",
64
+ },
65
65
  });
66
66
  ```
67
67
 
@@ -75,8 +75,8 @@ import type { StyleClassesMap } from "jassm";
75
75
  import "myStyles.css";
76
76
 
77
77
  export const stylesmap: StyleClassesMap = {
78
- header: "myHeader",
79
- paragraph: "pee",
78
+ header: "myHeader",
79
+ paragraph: "pee",
80
80
  };
81
81
  ```
82
82
 
@@ -88,8 +88,8 @@ import styles from "myStyles.module.css";
88
88
  import type { StyleClassesMap } from "jassm";
89
89
 
90
90
  export const stylesmap: StyleClassesMap = {
91
- header: styles.myHeader,
92
- paragraph: styles.pee,
91
+ header: styles.myHeader,
92
+ paragraph: styles.pee,
93
93
  };
94
94
  ```
95
95
 
@@ -143,17 +143,18 @@ import { stylesmap } from "./stylesmap";
143
143
  import { Suspense } from "react";
144
144
 
145
145
  export default function MyLoader() {
146
- const Component = registry["/root/sample"];
147
-
148
- return (
149
- <div>
150
- <StyleContext styles={stylesmap}>
151
- <Suspense>
152
- <Component components={Elements} />
153
- </Suspense>
154
- </StyleContext>
155
- </div>
156
- );
146
+ const Component = registry["/root/sample"];
147
+
148
+ return (
149
+ <div>
150
+ <StyleContext styles={stylesmap}>
151
+ <Suspense>
152
+ <Component components={Elements} />
153
+ </Suspense>
154
+ </StyleContext>
155
+ </div>
156
+ );
157
157
  }
158
158
  ```
159
+
159
160
  ---
package/dist/index.d.ts CHANGED
@@ -31,118 +31,201 @@ declare function generateElementsFrom<
31
31
  B extends boolean = true
32
32
  >(elements: E, baseElements?: B): { [K in B extends true ? keyof E | (typeof BaseElementTags)[number] : keyof E] : Element2 };
33
33
  /**
34
- * File extension(s) to accept
34
+ * Utility type that ensures a string starts with `/`
35
35
  */
36
- type MDXFile = `${string}.mdx`;
37
36
  type MustStartWithSlash<T extends string> = T extends `/${string}` ? T : never;
37
+ /**
38
+ * Utility type that ensures a string does NOT end with `/`
39
+ */
38
40
  type MustNotEndWithSlash<T extends string> = T extends `${string}/` ? never : T;
39
41
  /**
40
- * Options passed to {@link generateRegistries}
42
+ * Valid path that:
43
+ * - starts with `/`
44
+ * - does not end with `/`
45
+ */
46
+ type PathCheck<T extends string> = MustStartWithSlash<T> & MustNotEndWithSlash<T>;
47
+ /**
48
+ * Type returned by `import.meta.glob`
49
+ */
50
+ type WrappedUnknownPromise = () => Promise<unknown>;
51
+ /**
52
+ * Shape of glob result
53
+ */
54
+ type GlobResult<T> = Record<string, T>;
55
+ /**
56
+ * Removes `.mdx` extension from a path
57
+ */
58
+ type StripExtension<T extends string> = T extends `${infer P}.mdx` ? P : T;
59
+ /**
60
+ * Replace filesystem root with a virtual route prefix
61
+ *
62
+ * Example:
63
+ *
64
+ * `/src/content/foo.mdx`
65
+ * root = `/src/content`
66
+ * virtual = `/blog`
67
+ *
68
+ * Result:
69
+ * `/blog/foo`
70
+ */
71
+ type ReplaceRoot<
72
+ P extends string,
73
+ Root extends string,
74
+ Virtual extends string
75
+ > = P extends `${Root}${infer Rest}` ? `${Virtual}${StripExtension<Rest>}` : never;
76
+ /**
77
+ * Derives the route keys produced by the registry
78
+ *
79
+ * Example:
80
+ *
81
+ * `/src/blog/foo.mdx`
82
+ * → `/blog/foo`
83
+ */
84
+ type RouteKey<
85
+ Modules extends Record<string, unknown>,
86
+ Root extends string,
87
+ Virtual extends string
88
+ > = ReplaceRoot<Extract<keyof Modules, string>, Root, Virtual>;
89
+ /**
90
+ * Options passed to {@link generateRegistry}
41
91
  */
42
92
  type RegistryOptions<
43
- S extends string,
44
- M extends string,
45
- R extends Record<string, string>
93
+ MetaType,
94
+ Modules extends GlobResult<WrappedUnknownPromise>,
95
+ Root extends string,
96
+ Virtual extends string
46
97
  > = {
47
98
  /**
48
- * Module object returned from `import.meta.glob`
49
- * @example import.module.glob("/src/assets/mdx/**\/*.mdx")
99
+ * Module map returned from `import.meta.glob`
100
+ */
101
+ modulesGlob: Modules;
102
+ /**
103
+ * Metadata extracted from each MDX file
104
+ */
105
+ metadataGlob: { [K in keyof Modules] : MetaType };
106
+ /**
107
+ * Filesystem root path
108
+ */
109
+ root: PathCheck<Root>;
110
+ /**
111
+ * Virtual route mount point
112
+ */
113
+ virtual: PathCheck<Virtual>;
114
+ };
115
+ /**
116
+ * Internal function used by {@link Registry}.
117
+ *
118
+ * Builds registry mappings for:
119
+ * - keys
120
+ * - metadata
121
+ * - components
122
+ * - exports
123
+ */
124
+ declare function generateRegistry<
125
+ MetaType,
126
+ Modules extends GlobResult<WrappedUnknownPromise>,
127
+ Root extends string,
128
+ Virtual extends string
129
+ >({ modulesGlob, metadataGlob, root, virtual }: RegistryOptions<MetaType, Modules, Root, Virtual>): {
130
+ /**
131
+ * List of route keys
50
132
  */
51
- modules: Record<string, () => Promise<unknown>>;
133
+ keys: RouteKey<Modules, Root, Virtual>[];
52
134
  /**
53
- * Directory from which to resolve the source paths in {@link RegistryOptions.records}
135
+ * Metadata registry
54
136
  */
55
- source: MustNotEndWithSlash<S> & MustStartWithSlash<S>;
137
+ metadata: Record<RouteKey<Modules, Root, Virtual>, MetaType>;
56
138
  /**
57
- * Virtual base path on which to mount the key of {@link RegistryOptions.records}
139
+ * React lazy component registry
58
140
  */
59
- mountOn: MustNotEndWithSlash<M> & MustStartWithSlash<M>;
141
+ components: Record<RouteKey<Modules, Root, Virtual>, React.LazyExoticComponent<React.ComponentType>>;
60
142
  /**
61
- * Mappings of virtual path to file paths under {@link RegistryOptions.source}
143
+ * Raw module registry
62
144
  */
63
- records: { [K in keyof R] : K extends string ? MustNotEndWithSlash<K> & MustStartWithSlash<K> extends never ? never : R[K] extends MustStartWithSlash<R[K]> & MDXFile ? R[K] : never : never };
145
+ exports: Record<RouteKey<Modules, Root, Virtual>, Promise<Record<string, unknown>>>;
64
146
  };
65
- type RegistryKey<
66
- S extends string,
67
- M extends string,
68
- R extends Record<string, string>
69
- > = keyof RegistryOf<unknown, S, M, R>;
70
- /**
71
- * Constructor for any generic registry with keys in the format of `mount-path/virtual-path` for each virual path passed in {@link RegistryOptions.records}
72
- */
73
- type RegistryOf<
74
- T,
75
- S extends string,
76
- M extends string,
77
- R extends Record<string, string>
78
- > = { [K in keyof RegistryOptions<S, M, R>["records"] as `${RegistryOptions<S, M, R>["mountOn"]}${Extract<K, string>}`] : T };
79
- /**
80
- * Registry of react components
81
- */
82
- type ComponentRegistry<
83
- S extends string,
84
- M extends string,
85
- R extends Record<string, string>
86
- > = RegistryOf<React.LazyExoticComponent<React.ComponentType>, S, M, R>;
87
- /**
88
- * Registry of promise objects equivalent to return type of `import(<path>)`
89
- */
90
- type ExportsRegistry<
91
- S extends string,
92
- M extends string,
93
- R extends Record<string, string>
94
- > = RegistryOf<Promise<Record<string, unknown>>, S, M, R>;
95
- /**
96
- * Function to generate Registry mappings, use {@link Registry} class instead of this.
97
- * @param MDXRegistryOptions
98
- * @returns Tuple of [{@link ComponentRegistry}, {@link ExportsRegistry}]
99
- */
100
- declare function generateRegistries<
101
- S extends string,
102
- M extends string,
103
- R extends Record<string, string>
104
- >({ modules, source, mountOn, records }: RegistryOptions<S, M, R>): [ComponentRegistry<S, M, R>, ExportsRegistry<S, M, R>];
105
- /**
106
- * The returned object has the type of {@link React.ComponentType} + whatever user passes
107
- */
108
- type ExportSingleType<T> = Promise<T & {
109
- default: React.ComponentType;
110
- }>;
111
- type ExportAllType<T> = { [K in keyof T] : ExportSingleType<T[K]> };
147
+ /**
148
+ * Base class for all registry implementations.
149
+ *
150
+ * Provides type-safe access to:
151
+ * - components
152
+ * - module exports
153
+ * - metadata
154
+ */
112
155
  declare abstract class AbstractRegistry<
113
- C extends Record<string, React.LazyExoticComponent<React.ComponentType>>,
114
- E extends Record<string, unknown>
156
+ Keys extends string,
157
+ Components extends Record<Keys, React.LazyExoticComponent<React.ComponentType>>,
158
+ Exports extends Record<Keys, unknown>,
159
+ Metadata extends Record<Keys, unknown>
115
160
  > {
116
- abstract components: C;
117
- abstract exports: E;
118
- getComponent<K extends keyof C>(key: K): C[K];
119
- getComponents(): C;
120
- getExport<
121
- T extends object,
122
- K extends keyof E
123
- >(key: K): ExportSingleType<T>;
124
- getExports<T extends Record<keyof C, object> = Record<keyof C, object>>(): ExportAllType<T>;
161
+ /**
162
+ * List of registry keys
163
+ */
164
+ abstract readonly keys: readonly Keys[];
165
+ /**
166
+ * Lazy component registry
167
+ */
168
+ abstract readonly components: Components;
169
+ /**
170
+ * Raw module export registry
171
+ */
172
+ abstract readonly exports: Exports;
173
+ /**
174
+ * Metadata registry
175
+ */
176
+ abstract readonly metadata: Metadata;
177
+ /**
178
+ * Retrieve a React component by route key
179
+ */
180
+ getComponent<Key extends Keys>(key: Key): Components[Key];
181
+ /**
182
+ * Retrieve raw module exports for a route
183
+ */
184
+ getExport<Key extends Keys>(key: Key): Exports[Key];
185
+ /**
186
+ * Retrieve metadata for a route
187
+ */
188
+ getMetadata<Key extends Keys>(key: Key): Metadata[Key];
125
189
  }
126
190
  /**
127
- * Wrapper class over {@link generateRegistries}. Provides methods to access components and exports from typesafe keys
191
+ * Primary registry implementation.
192
+ *
193
+ * Wraps {@link generateRegistry} and exposes
194
+ * strongly typed access methods.
128
195
  */
129
196
  declare class Registry<
130
- S extends string,
131
- M extends string,
132
- R extends Record<string, string>
133
- > extends AbstractRegistry<ComponentRegistry<S, M, R>, ExportsRegistry<S, M, R>> {
134
- readonly components: ComponentRegistry<S, M, R>;
135
- readonly exports: ExportsRegistry<S, M, R>;
136
- constructor(registryOpts: RegistryOptions<S, M, R>);
197
+ MetaType,
198
+ Modules extends GlobResult<WrappedUnknownPromise>,
199
+ Root extends string,
200
+ Virtual extends string
201
+ > extends AbstractRegistry<RouteKey<Modules, Root, Virtual>, Record<RouteKey<Modules, Root, Virtual>, React.LazyExoticComponent<React.ComponentType>>, Record<RouteKey<Modules, Root, Virtual>, unknown>, Record<RouteKey<Modules, Root, Virtual>, MetaType>> {
202
+ readonly keys: RouteKey<Modules, Root, Virtual>[];
203
+ readonly components: Record<RouteKey<Modules, Root, Virtual>, React.LazyExoticComponent<React.ComponentType>>;
204
+ readonly exports: Record<RouteKey<Modules, Root, Virtual>, unknown>;
205
+ readonly metadata: Record<RouteKey<Modules, Root, Virtual>, MetaType>;
206
+ constructor(opts: RegistryOptions<MetaType, Modules, Root, Virtual>);
137
207
  }
138
- type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
139
208
  /**
140
- * Registry created by coalesing multiple {@link Registry} instances
209
+ * Utility types used for extracting generics
210
+ * from registry instances.
211
+ */
212
+ type RegistryKeys<R> = R extends AbstractRegistry<infer K, any, any, any> ? K : never;
213
+ type RegistryComponents<R> = R extends AbstractRegistry<any, infer C, any, any> ? C : never;
214
+ type RegistryExports<R> = R extends AbstractRegistry<any, any, infer E, any> ? E : never;
215
+ type RegistryMetadata<R> = R extends AbstractRegistry<any, any, any, infer M> ? M : never;
216
+ /**
217
+ * Registry created by merging multiple {@link Registry}
218
+ * instances into a single unified registry.
219
+ *
220
+ * Useful for combining multiple MDX content directories
221
+ * into one route registry.
141
222
  */
142
- declare class CoalescedRegistry<R extends readonly AbstractRegistry<Record<string, React.LazyExoticComponent<React.ComponentType>>, Record<string, unknown>>[]> extends AbstractRegistry<Record<keyof UnionToIntersection<R[number]["components"]>, React.LazyExoticComponent<React.ComponentType>>, Record<keyof UnionToIntersection<R[number]["exports"]>, unknown>> {
143
- readonly components: Record<keyof UnionToIntersection<R[number]["components"]>, React.LazyExoticComponent<React.ComponentType>>;
144
- readonly exports: Record<keyof UnionToIntersection<R[number]["exports"]>, unknown>;
145
- constructor(...registries: R);
223
+ declare class CoalescedRegistry<Registries extends AbstractRegistry<any, any, any, any>[]> extends AbstractRegistry<RegistryKeys<Registries[number]>, RegistryComponents<Registries[number]>, RegistryExports<Registries[number]>, RegistryMetadata<Registries[number]>> {
224
+ readonly keys: RegistryKeys<Registries[number]>[];
225
+ readonly components: RegistryComponents<Registries[number]>;
226
+ readonly exports: RegistryExports<Registries[number]>;
227
+ readonly metadata: RegistryMetadata<Registries[number]>;
228
+ constructor(...registries: Registries);
146
229
  }
147
230
  /**
148
231
  * List of default style classes
@@ -169,4 +252,4 @@ type MDXFromComponentProps = {
169
252
  * Simple way to directly load a component from the Registry
170
253
  */
171
254
  declare function MDXFromComponent({ source: SourceComponent, styles, fallback, elements }: MDXFromComponentProps): JSX;
172
- export { useStyles, generateRegistries, generateElementsFrom, StyleContext, StyleClassesMap, StyleClassesList, StyleClasses, RegistryOptions, RegistryOf, RegistryKey, Registry, MDXFromComponentProps, MDXFromComponent, MDXFile, MDXComponent, HeaderLevels, ExportsRegistry, ExportSingleType, ExportAllType, ElementProps, Element2 as Element, ComponentRegistry, CoalescedRegistry, BaseElements, BaseElementTags };
255
+ export { useStyles, generateRegistry, generateElementsFrom, StyleContext, StyleClassesMap, StyleClassesList, StyleClasses, RegistryOptions, Registry, MDXFromComponentProps, MDXFromComponent, MDXComponent, HeaderLevels, ElementProps, Element2 as Element, CoalescedRegistry, BaseElements, BaseElementTags };
package/dist/index.js CHANGED
@@ -505,57 +505,73 @@ function generateElementsFrom(elements, baseElements = true) {
505
505
  }
506
506
  // src/lib/Registry.ts
507
507
  import { lazy } from "react";
508
- function generateRegistries({
509
- modules,
510
- source,
511
- mountOn,
512
- records
508
+ function generateRegistry({
509
+ modulesGlob,
510
+ metadataGlob,
511
+ root,
512
+ virtual
513
513
  }) {
514
- const components = [];
515
- const exports = [];
516
- for (const [virtual, path] of Object.entries(records)) {
517
- const src = `${source}${path}`;
518
- const loader = modules[src];
519
- if (!loader) {
520
- throw new Error(`No such file exsits as ${src}`);
521
- }
522
- components.push([`${mountOn}${virtual}`, lazy(loader)]);
523
- exports.push([`${mountOn}${virtual}`, loader()]);
514
+ const paths = Object.keys(modulesGlob);
515
+ const keys = new Array(paths.length);
516
+ const _components = [];
517
+ const _exports = [];
518
+ const _metadata = [];
519
+ for (const [idx, path] of paths.entries()) {
520
+ const route = path.replace(root, virtual).replace(".mdx", "");
521
+ const loader = modulesGlob[path];
522
+ keys[idx] = route;
523
+ _components.push([route, lazy(loader)]);
524
+ _exports.push([route, loader()]);
525
+ _metadata.push([route, metadataGlob[path]]);
524
526
  }
525
- return [Object.fromEntries(components), Object.fromEntries(exports)];
527
+ return {
528
+ keys,
529
+ components: Object.fromEntries(_components),
530
+ exports: Object.fromEntries(_exports),
531
+ metadata: Object.fromEntries(_metadata)
532
+ };
526
533
  }
527
534
 
528
535
  class AbstractRegistry {
529
536
  getComponent(key) {
530
537
  return this.components[key];
531
538
  }
532
- getComponents() {
533
- return this.components;
534
- }
535
539
  getExport(key) {
536
540
  return this.exports[key];
537
541
  }
538
- getExports() {
539
- return this.exports;
542
+ getMetadata(key) {
543
+ return this.metadata[key];
540
544
  }
541
545
  }
542
546
 
543
547
  class Registry extends AbstractRegistry {
548
+ keys;
544
549
  components;
545
550
  exports;
546
- constructor(registryOpts) {
551
+ metadata;
552
+ constructor(opts) {
547
553
  super();
548
- [this.components, this.exports] = generateRegistries(registryOpts);
554
+ const result = generateRegistry(opts);
555
+ this.keys = result.keys;
556
+ this.components = result.components;
557
+ this.exports = result.exports;
558
+ this.metadata = result.metadata;
549
559
  }
550
560
  }
551
561
 
552
562
  class CoalescedRegistry extends AbstractRegistry {
553
- components;
554
- exports;
563
+ keys = [];
564
+ components = {};
565
+ exports = {};
566
+ metadata = {};
555
567
  constructor(...registries) {
556
568
  super();
557
- this.components = Object.assign({}, ...registries.map((r) => r.getComponents()));
558
- this.exports = Object.assign({}, ...registries.map((r) => r.getExports()));
569
+ for (const registry of registries) {
570
+ this.keys.push(...registry.keys);
571
+ Object.assign(this.components, registry.components);
572
+ Object.assign(this.exports, registry.exports);
573
+ Object.assign(this.metadata, registry.metadata);
574
+ }
559
575
  }
560
576
  }
561
577
  // src/components/Loader.tsx
@@ -578,7 +594,7 @@ function MDXFromComponent({
578
594
  }
579
595
  export {
580
596
  useStyles,
581
- generateRegistries,
597
+ generateRegistry,
582
598
  generateElementsFrom,
583
599
  StyleContext,
584
600
  StyleClassesList,
package/package.json CHANGED
@@ -1,11 +1,17 @@
1
1
  {
2
2
  "name": "@d1vij/jassm",
3
+ "version": "0.1.18-beta.1",
3
4
  "description": "Just another static site maker. Create simple content driven sites using MDX and React along with Typescript safety.",
4
- "version": "0.1.17",
5
- "type": "module",
5
+ "homepage": "https://github.com/d1vij/jassm",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/d1vij/jassm.git"
10
+ },
6
11
  "files": [
7
12
  "dist"
8
13
  ],
14
+ "type": "module",
9
15
  "module": "./dist/index.js",
10
16
  "types": "./dist/index.d.ts",
11
17
  "exports": {
@@ -16,17 +22,11 @@
16
22
  }
17
23
  },
18
24
  "./plugin": {
19
- "import": "./dist/vitePlugin.js",
20
- "types": "./dist/vitePlugin.d.ts"
25
+ "types": "./dist/vitePlugin.d.ts",
26
+ "import": "./dist/vitePlugin.js"
21
27
  },
22
28
  "./package.json": "./package.json"
23
29
  },
24
- "repository": {
25
- "type": "git",
26
- "url": "git+https://github.com/d1vij/jassm.git"
27
- },
28
- "homepage": "https://github.com/d1vij/jassm",
29
- "license": "MIT",
30
30
  "publishConfig": {
31
31
  "access": "public"
32
32
  },
@@ -41,8 +41,11 @@
41
41
  "lint": "biome check --fix --unsafe && tsc -b",
42
42
  "format": "biome format --write"
43
43
  },
44
+ "dependencies": {
45
+ "@d1vij/shit-i-always-use": "^0.1.3"
46
+ },
44
47
  "devDependencies": {
45
- "@biomejs/biome": "^2.4.4",
48
+ "@biomejs/biome": "^2.4.6",
46
49
  "@types/bun": "^1.3.9",
47
50
  "@types/mdx": "^2.0.13",
48
51
  "@types/react": "^19.2.14",
@@ -65,9 +68,6 @@
65
68
  "optional": true
66
69
  }
67
70
  },
68
- "dependencies": {
69
- "@d1vij/shit-i-always-use": "^0.1.3"
70
- },
71
71
  "simple-git-hooks": {
72
72
  "pre-commit": "bun run lint && bun run type-check"
73
73
  }