@fuzdev/fuz_ui 0.195.1 → 0.196.0

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.
@@ -1,143 +0,0 @@
1
- /**
2
- * Gro-specific library metadata generation.
3
- *
4
- * This module provides Gro integration for library generation. It uses svelte-docinfo's
5
- * pure analysis (`analyze`) and wraps the results with fuz_ui's opinionated
6
- * LibraryJson format (GitHub/npm metadata).
7
- *
8
- * For build-tool agnostic usage, see `svelte-docinfo`.
9
- *
10
- * @see svelte-docinfo/analyze.js for the generic analysis entry point
11
- * @see svelte-docinfo/postprocess.js for post-processing helpers
12
- * @see library_output.js for output file generation
13
- *
14
- * @module
15
- */
16
- import { package_json_load } from '@fuzdev/gro/package_json.js';
17
- import { analyze, createSourceOptions, } from 'svelte-docinfo';
18
- import { normalizeSourceOptions, isSource, getSourceRoot } from 'svelte-docinfo/source-config.js';
19
- import { library_generate_output } from './library_output.js';
20
- /**
21
- * Convert Gro's `Disknode` to the build-tool agnostic `SourceFileInfo` interface.
22
- *
23
- * Use this when you want to analyze files using Gro's filer directly. The
24
- * `dependencies` field is populated from the filer's forward-edge graph — the
25
- * svelte-docinfo session honors it as pre-resolved input and skips its own
26
- * lex+resolve pass for these files, avoiding duplicate work the filer already did.
27
- *
28
- * Reverse edges (`dependents`) are not threaded through — svelte-docinfo
29
- * computes them internally from the forward edges of the owned set.
30
- *
31
- * @throws Error if disknode has no content (should be loaded by Gro filer)
32
- */
33
- export const source_file_from_disknode = (disknode) => {
34
- if (disknode.contents == null) {
35
- throw new Error(`Source file has no content: ${disknode.id} (ensure Gro filer loads file contents)`);
36
- }
37
- return {
38
- id: disknode.id,
39
- content: disknode.contents,
40
- dependencies: [...disknode.dependencies.keys()],
41
- };
42
- };
43
- /**
44
- * Collect source files from Gro disknodes, filtering BEFORE conversion to `SourceFileInfo`.
45
- *
46
- * This avoids errors from files outside source directories (like test fixtures that may
47
- * have malformed paths or missing content). The filtering uses `isSource` which
48
- * checks `source_paths` to only include files in configured source directories.
49
- *
50
- * @param disknodes - iterator of Gro disknodes from filer
51
- * @param options - module source options for filtering
52
- * @param log - optional logger for status messages
53
- */
54
- export const library_collect_source_files_from_disknodes = (disknodes, options, log) => {
55
- // Normalize options (throws on invalid config) and use the normalized form
56
- // for downstream `isSource` / `getSourceRoot` calls, so callers that pass
57
- // raw options (not via `createSourceOptions`) get consistent path handling.
58
- const normalized_options = normalizeSourceOptions(options);
59
- const all_disknodes = Array.from(disknodes);
60
- log?.info(`received ${all_disknodes.length} files total from filer`);
61
- const source_files = [];
62
- for (const disknode of all_disknodes) {
63
- // Filter by source_paths BEFORE trying to convert
64
- // This avoids errors from test fixtures or other non-source files
65
- if (!isSource(disknode.id, normalized_options)) {
66
- continue;
67
- }
68
- source_files.push(source_file_from_disknode(disknode));
69
- }
70
- log?.info(`found ${source_files.length} source files to analyze`);
71
- if (source_files.length === 0) {
72
- const effective_root = getSourceRoot(normalized_options);
73
- log?.warn(`No source files found in ${effective_root} - generating empty library metadata`);
74
- return [];
75
- }
76
- // Sort for deterministic output (stable alphabetical module ordering)
77
- source_files.sort((a, b) => a.id.localeCompare(b.id));
78
- return source_files;
79
- };
80
- /**
81
- * Creates a `Gen` object for generating library metadata with full TypeScript analysis.
82
- *
83
- * This is the Gro-specific entry point. It handles:
84
- * - Reading files from Gro's filer
85
- * - Loading package.json via Gro utilities
86
- * - Analyzing source with svelte-docinfo (pure analysis)
87
- * - Wrapping with LibraryJson (GitHub/npm metadata)
88
- * - Returning output in Gro's Gen format
89
- *
90
- * For build-tool agnostic usage, use `analyze` directly.
91
- *
92
- * Usage in a `.gen.ts` file:
93
- *
94
- * ```ts
95
- * import {library_gen} from './library_gen.js';
96
- *
97
- * export const gen = library_gen();
98
- * ```
99
- *
100
- * @param options - optional generation options
101
- */
102
- export const library_gen = (options) => {
103
- return {
104
- dependencies: 'all',
105
- generate: async ({ log, filer }) => {
106
- log.info('generating library metadata with full TypeScript analysis...');
107
- // Build source options with project_root from cwd
108
- const source_options = options?.source && 'projectRoot' in options.source
109
- ? options.source
110
- : createSourceOptions(process.cwd(), options?.source);
111
- // Ensure filer is initialized
112
- await filer.init();
113
- // Read package.json
114
- const package_json = await package_json_load();
115
- // Collect source files from Gro filer
116
- const source_files = library_collect_source_files_from_disknodes(filer.files.values(), source_options, log);
117
- // Get pure analysis from svelte-docinfo (no package metadata).
118
- const { modules } = await analyze({
119
- sourceFiles: source_files,
120
- sourceOptions: source_options,
121
- onDuplicates: options?.on_duplicates,
122
- log: log, // Type cast needed due to workspace dependency duplication
123
- });
124
- if (!package_json.version) {
125
- throw new Error('package.json is missing required "version" field');
126
- }
127
- // Wrap modules with package metadata (fuz_ui's own SourceJson type)
128
- const source_json = {
129
- name: package_json.name,
130
- version: package_json.version,
131
- repository: typeof package_json.repository === 'string'
132
- ? package_json.repository
133
- : package_json.repository?.url,
134
- modules: modules, // TODO: remove cast when fuz_util SourceJson uses svelte-docinfo types
135
- };
136
- // Generate output files with fuz_ui's LibraryJson wrapper
137
- const { json_content, ts_content } = library_generate_output(package_json, source_json);
138
- log.info('library metadata generation complete');
139
- // Return array of files in Gro's expected format
140
- return [{ content: ts_content }, { content: json_content, filename: 'library.json' }];
141
- },
142
- };
143
- };
@@ -1,36 +0,0 @@
1
- /**
2
- * Library output generation.
3
- *
4
- * Generates the library.json and library.ts files from analyzed metadata.
5
- * Uses svelte-docinfo's `compactReplacer` to strip Zod default values
6
- * (empty arrays, false booleans) for compact JSON output.
7
- *
8
- * @see `library_gen.ts` for the main generation entry point
9
- * @see {@link https://github.com/ryanatkn/svelte-docinfo svelte-docinfo} for the analysis library
10
- *
11
- * @module
12
- */
13
- import type { PackageJson } from '@fuzdev/fuz_util/package_json.js';
14
- import type { SourceJson } from '@fuzdev/fuz_util/source_json.js';
15
- /**
16
- * Result of generating library output files.
17
- * Contains both the JSON data and the TypeScript wrapper file.
18
- */
19
- export interface LibraryOutputResult {
20
- /** JSON content for `library.json`. */
21
- json_content: string;
22
- /** TypeScript wrapper content for `library.ts`. */
23
- ts_content: string;
24
- }
25
- /**
26
- * Generate the `library.json` and `library.ts` file contents.
27
- * Parses at generation time so runtime only needs the pre-computed result.
28
- *
29
- * Returns JSON + .ts wrapper because:
30
- * - JSON is natively importable by Node.js and Vite without TypeScript loaders
31
- * - Works in CI environments that don't have TS compilation
32
- * - The .ts wrapper validates with zod and exports with proper types
33
- * (JSON imports get widened types like `string` instead of literal unions)
34
- */
35
- export declare const library_generate_output: (package_json: PackageJson, source_json: SourceJson) => LibraryOutputResult;
36
- //# sourceMappingURL=library_output.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"library_output.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/library_output.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,kCAAkC,CAAC;AAClE,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,iCAAiC,CAAC;AAIhE;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IACnC,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,GACnC,cAAc,WAAW,EACzB,aAAa,UAAU,KACrB,mBAwBF,CAAC"}
@@ -1,44 +0,0 @@
1
- /**
2
- * Library output generation.
3
- *
4
- * Generates the library.json and library.ts files from analyzed metadata.
5
- * Uses svelte-docinfo's `compactReplacer` to strip Zod default values
6
- * (empty arrays, false booleans) for compact JSON output.
7
- *
8
- * @see `library_gen.ts` for the main generation entry point
9
- * @see {@link https://github.com/ryanatkn/svelte-docinfo svelte-docinfo} for the analysis library
10
- *
11
- * @module
12
- */
13
- import { library_json_parse } from '@fuzdev/fuz_util/library_json.js';
14
- import { compactReplacer } from 'svelte-docinfo';
15
- /**
16
- * Generate the `library.json` and `library.ts` file contents.
17
- * Parses at generation time so runtime only needs the pre-computed result.
18
- *
19
- * Returns JSON + .ts wrapper because:
20
- * - JSON is natively importable by Node.js and Vite without TypeScript loaders
21
- * - Works in CI environments that don't have TS compilation
22
- * - The .ts wrapper validates with zod and exports with proper types
23
- * (JSON imports get widened types like `string` instead of literal unions)
24
- */
25
- export const library_generate_output = (package_json, source_json) => {
26
- // Compact source_json (strips Zod default values like empty arrays and false booleans)
27
- // Only applied to source_json, not the outer library_json package metadata
28
- const compacted_source_json = JSON.parse(JSON.stringify(source_json, compactReplacer));
29
- // Parse at generation time, not runtime
30
- const library_json = library_json_parse(package_json, compacted_source_json);
31
- const json_content = JSON.stringify(library_json, null, '\t') + '\n';
32
- const banner = '// generated by library.gen.ts - do not edit';
33
- const ts_content = `${banner}
34
-
35
- import type {LibraryJson} from '@fuzdev/fuz_util/library_json.js';
36
-
37
- import json from './library.json' with {type: 'json'};
38
-
39
- export const library_json: LibraryJson = json as LibraryJson;
40
-
41
- ${banner}
42
- `;
43
- return { json_content, ts_content };
44
- };
@@ -1,37 +0,0 @@
1
- import type { Plugin } from 'vite';
2
- export interface VitePluginLibraryWellKnownOptions {
3
- /**
4
- * Path to the `library.json` file (relative to `vite.config.ts`).
5
- * @default './src/routes/library.json'
6
- */
7
- library_path?: string;
8
- }
9
- /**
10
- * Vite plugin that publishes `package.json` and `library.json` to `.well-known/`.
11
- *
12
- * Requires a generated `library.json` file (created by `library_gen` from `gro gen`).
13
- * The plugin reads this JSON file and publishes its metadata to `.well-known/` for
14
- * both dev and production builds.
15
- *
16
- * Note: This plugin respects SvelteKit's `base` path configuration, so `.well-known/`
17
- * will be served at `{base}.well-known/` (e.g., `/my-app/.well-known/`). This deviates
18
- * from RFC 8615 which specifies `.well-known` should be at the domain root. This tradeoff
19
- * allows the plugin to work correctly when apps are deployed to non-root paths.
20
- *
21
- * Note: CORS headers are only set for the dev server. For production, configure
22
- * CORS at the server level (nginx, Caddy, etc.) if cross-origin access is needed.
23
- *
24
- * @example
25
- * ```ts
26
- * // vite.config.ts
27
- * import {defineConfig} from 'vite';
28
- * import {sveltekit} from '@sveltejs/kit/vite';
29
- * import {vite_plugin_library_well_known} from './vite_plugin_library_well_known.js';
30
- *
31
- * export default defineConfig({
32
- * plugins: [sveltekit(), vite_plugin_library_well_known()],
33
- * });
34
- * ```
35
- */
36
- export declare const vite_plugin_library_well_known: (options?: VitePluginLibraryWellKnownOptions) => Plugin;
37
- //# sourceMappingURL=vite_plugin_library_well_known.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"vite_plugin_library_well_known.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/vite_plugin_library_well_known.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,MAAM,CAAC;AAMjC,MAAM,WAAW,iCAAiC;IACjD;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAcD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,8BAA8B,GAC1C,UAAS,iCAAsC,KAC7C,MAqIF,CAAC"}
@@ -1,144 +0,0 @@
1
- import { isAbsolute, join } from 'node:path';
2
- import { readFile } from 'node:fs/promises';
3
- const respond_json = (res, body) => {
4
- res.setHeader('Content-Type', 'application/json');
5
- res.setHeader('Cache-Control', 'no-store');
6
- res.setHeader('Access-Control-Allow-Origin', '*');
7
- res.end(body);
8
- };
9
- /**
10
- * Vite plugin that publishes `package.json` and `library.json` to `.well-known/`.
11
- *
12
- * Requires a generated `library.json` file (created by `library_gen` from `gro gen`).
13
- * The plugin reads this JSON file and publishes its metadata to `.well-known/` for
14
- * both dev and production builds.
15
- *
16
- * Note: This plugin respects SvelteKit's `base` path configuration, so `.well-known/`
17
- * will be served at `{base}.well-known/` (e.g., `/my-app/.well-known/`). This deviates
18
- * from RFC 8615 which specifies `.well-known` should be at the domain root. This tradeoff
19
- * allows the plugin to work correctly when apps are deployed to non-root paths.
20
- *
21
- * Note: CORS headers are only set for the dev server. For production, configure
22
- * CORS at the server level (nginx, Caddy, etc.) if cross-origin access is needed.
23
- *
24
- * @example
25
- * ```ts
26
- * // vite.config.ts
27
- * import {defineConfig} from 'vite';
28
- * import {sveltekit} from '@sveltejs/kit/vite';
29
- * import {vite_plugin_library_well_known} from './vite_plugin_library_well_known.js';
30
- *
31
- * export default defineConfig({
32
- * plugins: [sveltekit(), vite_plugin_library_well_known()],
33
- * });
34
- * ```
35
- */
36
- export const vite_plugin_library_well_known = (options = {}) => {
37
- const { library_path = './src/routes/library.json' } = options;
38
- const content = {
39
- package_json: null,
40
- library_json: null,
41
- };
42
- // Set by configResolved
43
- let root;
44
- let base; // e.g., '/' or '/my-app/'
45
- // Promise that resolves when library is loaded, used by middleware to avoid race condition
46
- let ready_promise = null;
47
- const load_library = async () => {
48
- // Resolve path relative to project root
49
- const resolved_path = isAbsolute(library_path) ? library_path : join(root, library_path);
50
- let json_content;
51
- try {
52
- json_content = await readFile(resolved_path, 'utf-8');
53
- }
54
- catch (error) {
55
- throw new Error(`vite_plugin_library_well_known: failed to read library.json from "${library_path}"\n` +
56
- `Resolved to: ${resolved_path}\n` +
57
- `Make sure you've run \`gro gen\` to generate the library metadata.\n` +
58
- `Error: ${error}`);
59
- }
60
- let raw;
61
- try {
62
- raw = JSON.parse(json_content);
63
- }
64
- catch (error) {
65
- throw new Error(`vite_plugin_library_well_known: failed to parse library.json from "${library_path}"\n` +
66
- `Error: ${error}`);
67
- }
68
- // Basic structure validation (file is generated by library_gen)
69
- if (raw == null || typeof raw !== 'object') {
70
- throw new Error(`vite_plugin_library_well_known: invalid library.json at "${library_path}"\n` +
71
- `Expected object, got ${typeof raw}`);
72
- }
73
- if (!('package_json' in raw) || !('source_json' in raw)) {
74
- throw new Error(`vite_plugin_library_well_known: invalid library.json at "${library_path}"\n` +
75
- `Expected object with package_json and source_json properties`);
76
- }
77
- const library_json = raw;
78
- content.package_json = JSON.stringify(library_json.package_json, null, '\t') + '\n';
79
- content.library_json = JSON.stringify(library_json, null, '\t') + '\n';
80
- };
81
- return {
82
- name: 'vite_plugin_library_well_known',
83
- configResolved(config) {
84
- root = config.root;
85
- base = config.base; // e.g., '/' or '/my-app/'
86
- },
87
- async buildStart() {
88
- ready_promise = load_library();
89
- await ready_promise;
90
- },
91
- configureServer(server) {
92
- // TODO: add HMR support to reload library.json when it changes
93
- const well_known_prefix = `${base}.well-known/`;
94
- server.middlewares.use(async (req, res, next) => {
95
- if (!req.url) {
96
- next();
97
- return;
98
- }
99
- const { pathname } = new URL(req.url, 'http://localhost');
100
- if (!pathname.startsWith(well_known_prefix)) {
101
- next();
102
- return;
103
- }
104
- // Wait for library to load before serving
105
- try {
106
- if (!ready_promise)
107
- throw new Error('not initialized');
108
- await ready_promise;
109
- }
110
- catch (_error) {
111
- res.statusCode = 503;
112
- respond_json(res, JSON.stringify({ error: 'Library not ready' }));
113
- return;
114
- }
115
- const path = pathname.slice(well_known_prefix.length);
116
- if (path === 'package.json' && content.package_json) {
117
- respond_json(res, content.package_json);
118
- return;
119
- }
120
- if (path === 'library.json' && content.library_json) {
121
- respond_json(res, content.library_json);
122
- return;
123
- }
124
- next();
125
- });
126
- },
127
- generateBundle() {
128
- if (content.package_json) {
129
- this.emitFile({
130
- type: 'asset',
131
- fileName: '.well-known/package.json',
132
- source: content.package_json,
133
- });
134
- }
135
- if (content.library_json) {
136
- this.emitFile({
137
- type: 'asset',
138
- fileName: '.well-known/library.json',
139
- source: content.library_json,
140
- });
141
- }
142
- },
143
- };
144
- };
@@ -1,219 +0,0 @@
1
- /**
2
- * Gro-specific library metadata generation.
3
- *
4
- * This module provides Gro integration for library generation. It uses svelte-docinfo's
5
- * pure analysis (`analyze`) and wraps the results with fuz_ui's opinionated
6
- * LibraryJson format (GitHub/npm metadata).
7
- *
8
- * For build-tool agnostic usage, see `svelte-docinfo`.
9
- *
10
- * @see svelte-docinfo/analyze.js for the generic analysis entry point
11
- * @see svelte-docinfo/postprocess.js for post-processing helpers
12
- * @see library_output.js for output file generation
13
- *
14
- * @module
15
- */
16
-
17
- import type {Gen} from '@fuzdev/gro';
18
- import {package_json_load} from '@fuzdev/gro/package_json.js';
19
- import type {Disknode} from '@fuzdev/gro/disknode.js';
20
- import {
21
- analyze,
22
- createSourceOptions,
23
- type ModuleSourceOptions,
24
- type OnDuplicatesCallback,
25
- type SourceFileInfo,
26
- type SourceOptionsDefaults,
27
- } from 'svelte-docinfo';
28
- import {normalizeSourceOptions, isSource, getSourceRoot} from 'svelte-docinfo/source-config.js';
29
- import type {SourceJson} from '@fuzdev/fuz_util/source_json.js';
30
-
31
- import {library_generate_output} from './library_output.js';
32
-
33
- /** Options for Gro library generation. */
34
- export interface LibraryGenOptions {
35
- /**
36
- * Module source options for filtering and path extraction.
37
- *
38
- * Can provide full `ModuleSourceOptions` or partial options that will be
39
- * merged with defaults. The `project_root` is automatically set to
40
- * `process.cwd()` if not provided.
41
- */
42
- source?: ModuleSourceOptions | Partial<SourceOptionsDefaults>;
43
- /**
44
- * Callback invoked when duplicate declaration names are found.
45
- *
46
- * Consumers decide how to handle duplicates: throw, warn, or ignore.
47
- * Use `throwOnDuplicates` for strict flat namespace enforcement.
48
- *
49
- * @example
50
- * ```ts
51
- * // Throw on duplicates (strict flat namespace)
52
- * library_gen({ on_duplicates: throwOnDuplicates });
53
- *
54
- * // Warn but continue
55
- * library_gen({
56
- * on_duplicates: (dupes, log) => {
57
- * for (const [name, locs] of dupes) {
58
- * log.warn(`Duplicate: ${name} in ${locs.map(l => l.module).join(', ')}`);
59
- * }
60
- * }
61
- * });
62
- * ```
63
- */
64
- on_duplicates?: OnDuplicatesCallback;
65
- }
66
-
67
- /**
68
- * Convert Gro's `Disknode` to the build-tool agnostic `SourceFileInfo` interface.
69
- *
70
- * Use this when you want to analyze files using Gro's filer directly. The
71
- * `dependencies` field is populated from the filer's forward-edge graph — the
72
- * svelte-docinfo session honors it as pre-resolved input and skips its own
73
- * lex+resolve pass for these files, avoiding duplicate work the filer already did.
74
- *
75
- * Reverse edges (`dependents`) are not threaded through — svelte-docinfo
76
- * computes them internally from the forward edges of the owned set.
77
- *
78
- * @throws Error if disknode has no content (should be loaded by Gro filer)
79
- */
80
- export const source_file_from_disknode = (disknode: Disknode): SourceFileInfo => {
81
- if (disknode.contents == null) {
82
- throw new Error(
83
- `Source file has no content: ${disknode.id} (ensure Gro filer loads file contents)`,
84
- );
85
- }
86
- return {
87
- id: disknode.id,
88
- content: disknode.contents,
89
- dependencies: [...disknode.dependencies.keys()],
90
- };
91
- };
92
-
93
- /**
94
- * Collect source files from Gro disknodes, filtering BEFORE conversion to `SourceFileInfo`.
95
- *
96
- * This avoids errors from files outside source directories (like test fixtures that may
97
- * have malformed paths or missing content). The filtering uses `isSource` which
98
- * checks `source_paths` to only include files in configured source directories.
99
- *
100
- * @param disknodes - iterator of Gro disknodes from filer
101
- * @param options - module source options for filtering
102
- * @param log - optional logger for status messages
103
- */
104
- export const library_collect_source_files_from_disknodes = (
105
- disknodes: Iterable<Disknode>,
106
- options: ModuleSourceOptions,
107
- log?: {info: (...args: Array<unknown>) => void; warn: (...args: Array<unknown>) => void},
108
- ): Array<SourceFileInfo> => {
109
- // Normalize options (throws on invalid config) and use the normalized form
110
- // for downstream `isSource` / `getSourceRoot` calls, so callers that pass
111
- // raw options (not via `createSourceOptions`) get consistent path handling.
112
- const normalized_options = normalizeSourceOptions(options);
113
-
114
- const all_disknodes = Array.from(disknodes);
115
- log?.info(`received ${all_disknodes.length} files total from filer`);
116
-
117
- const source_files: Array<SourceFileInfo> = [];
118
- for (const disknode of all_disknodes) {
119
- // Filter by source_paths BEFORE trying to convert
120
- // This avoids errors from test fixtures or other non-source files
121
- if (!isSource(disknode.id, normalized_options)) {
122
- continue;
123
- }
124
- source_files.push(source_file_from_disknode(disknode));
125
- }
126
-
127
- log?.info(`found ${source_files.length} source files to analyze`);
128
-
129
- if (source_files.length === 0) {
130
- const effective_root = getSourceRoot(normalized_options);
131
- log?.warn(`No source files found in ${effective_root} - generating empty library metadata`);
132
- return [];
133
- }
134
-
135
- // Sort for deterministic output (stable alphabetical module ordering)
136
- source_files.sort((a, b) => a.id.localeCompare(b.id));
137
-
138
- return source_files;
139
- };
140
-
141
- /**
142
- * Creates a `Gen` object for generating library metadata with full TypeScript analysis.
143
- *
144
- * This is the Gro-specific entry point. It handles:
145
- * - Reading files from Gro's filer
146
- * - Loading package.json via Gro utilities
147
- * - Analyzing source with svelte-docinfo (pure analysis)
148
- * - Wrapping with LibraryJson (GitHub/npm metadata)
149
- * - Returning output in Gro's Gen format
150
- *
151
- * For build-tool agnostic usage, use `analyze` directly.
152
- *
153
- * Usage in a `.gen.ts` file:
154
- *
155
- * ```ts
156
- * import {library_gen} from '@fuzdev/fuz_ui/library_gen.js';
157
- *
158
- * export const gen = library_gen();
159
- * ```
160
- *
161
- * @param options - optional generation options
162
- */
163
- export const library_gen = (options?: LibraryGenOptions): Gen => {
164
- return {
165
- dependencies: 'all',
166
- generate: async ({log, filer}) => {
167
- log.info('generating library metadata with full TypeScript analysis...');
168
-
169
- // Build source options with project_root from cwd
170
- const source_options: ModuleSourceOptions =
171
- options?.source && 'projectRoot' in options.source
172
- ? options.source
173
- : createSourceOptions(process.cwd(), options?.source);
174
-
175
- // Ensure filer is initialized
176
- await filer.init();
177
-
178
- // Read package.json
179
- const package_json = await package_json_load();
180
-
181
- // Collect source files from Gro filer
182
- const source_files = library_collect_source_files_from_disknodes(
183
- filer.files.values(),
184
- source_options,
185
- log,
186
- );
187
-
188
- // Get pure analysis from svelte-docinfo (no package metadata).
189
- const {modules} = await analyze({
190
- sourceFiles: source_files,
191
- sourceOptions: source_options,
192
- onDuplicates: options?.on_duplicates,
193
- log: log as any, // Type cast needed due to workspace dependency duplication
194
- });
195
-
196
- if (!package_json.version) {
197
- throw new Error('package.json is missing required "version" field');
198
- }
199
- // Wrap modules with package metadata (fuz_ui's own SourceJson type)
200
- const source_json: SourceJson = {
201
- name: package_json.name,
202
- version: package_json.version,
203
- repository:
204
- typeof package_json.repository === 'string'
205
- ? package_json.repository
206
- : package_json.repository?.url,
207
- modules: modules as any, // TODO: remove cast when fuz_util SourceJson uses svelte-docinfo types
208
- };
209
-
210
- // Generate output files with fuz_ui's LibraryJson wrapper
211
- const {json_content, ts_content} = library_generate_output(package_json, source_json);
212
-
213
- log.info('library metadata generation complete');
214
-
215
- // Return array of files in Gro's expected format
216
- return [{content: ts_content}, {content: json_content, filename: 'library.json'}];
217
- },
218
- };
219
- };