pointfree-docs 0.1.0 → 0.2.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.
@@ -3,11 +3,25 @@
3
3
  */
4
4
  import chalk from "chalk";
5
5
  import { search as searchIndex, withIndex } from "../lib/index.js";
6
+ import { SOURCE_TYPES } from "../config.js";
7
+ import { getSourceLabel } from "../lib/format.js";
6
8
  export function searchCommand(query, options) {
7
9
  const limit = options.limit ? parseInt(options.limit, 10) : 10;
8
- const results = withIndex(() => searchIndex(query, { lib: options.lib, limit }));
10
+ // Validate source option
11
+ let source = "docs"; // Default to docs only
12
+ if (options.source) {
13
+ if (options.source === "all" || SOURCE_TYPES.includes(options.source)) {
14
+ source = options.source;
15
+ }
16
+ else {
17
+ console.log(chalk.red(`Invalid source: ${options.source}`));
18
+ console.log(chalk.gray(`Valid sources: ${SOURCE_TYPES.join(", ")}, all`));
19
+ return;
20
+ }
21
+ }
22
+ const results = withIndex(() => searchIndex(query, { lib: options.lib, limit, source }));
9
23
  if (options.json) {
10
- console.log(JSON.stringify({ query, results }, null, 2));
24
+ console.log(JSON.stringify({ query, source, results }, null, 2));
11
25
  return;
12
26
  }
13
27
  if (results.length === 0) {
@@ -15,17 +29,26 @@ export function searchCommand(query, options) {
15
29
  if (options.lib) {
16
30
  console.log(chalk.gray(` (searched in library: ${options.lib})`));
17
31
  }
32
+ if (source !== "all") {
33
+ console.log(chalk.gray(` (searched in source: ${source})`));
34
+ console.log(chalk.gray(` Try --source=all to search everything`));
35
+ }
18
36
  console.log(chalk.gray(`\nTry a different query or run 'pf-docs list' to see available docs.`));
19
37
  return;
20
38
  }
21
- console.log(chalk.bold(`\nšŸ” Search results for: "${query}"\n`));
39
+ const sourceLabel = source === "all" ? "all sources" : source;
40
+ console.log(chalk.bold(`\nšŸ” Search results for: "${query}" (${sourceLabel})\n`));
22
41
  for (let i = 0; i < results.length; i++) {
23
42
  const result = results[i];
24
- console.log(chalk.blue(`${i + 1}. ${result.title}`));
43
+ const label = source === "all" ? `${getSourceLabel(result.source)} ` : "";
44
+ console.log(`${label}${chalk.blue(`${i + 1}. ${result.title}`)}`);
25
45
  console.log(chalk.gray(` Path: ${result.path}`));
26
46
  console.log(chalk.gray(` ${result.snippet}`));
27
47
  console.log();
28
48
  }
29
49
  console.log(chalk.gray(`\nTo view a document: pf-docs get <path>`));
30
50
  console.log(chalk.gray(`Example: pf-docs get ${results[0].path}`));
51
+ if (source !== "all") {
52
+ console.log(chalk.gray(`\nTip: Use --source=all to search docs, examples, and episodes together`));
53
+ }
31
54
  }
@@ -3,8 +3,9 @@
3
3
  */
4
4
  import chalk from "chalk";
5
5
  import { getStats, withIndex } from "../lib/index.js";
6
- import { LIBRARIES, getLibrary } from "../config.js";
7
- import { isLibraryCloned } from "../lib/repos.js";
6
+ import { LIBRARIES, getLibrary, EXAMPLES_CONFIG, EPISODES_CONFIG } from "../config.js";
7
+ import { isLibraryCloned, areExamplesCloned, areEpisodesCloned } from "../lib/repos.js";
8
+ import { getSourceName } from "../lib/format.js";
8
9
  export function statsCommand(options) {
9
10
  const stats = withIndex(() => getStats());
10
11
  if (options.json) {
@@ -12,12 +13,19 @@ export function statsCommand(options) {
12
13
  return;
13
14
  }
14
15
  console.log(chalk.bold("\nšŸ“Š pf-docs Statistics\n"));
15
- console.log(chalk.blue(`Total indexed documents: ${stats.totalDocs}`));
16
+ console.log(chalk.blue(`Total indexed items: ${stats.totalDocs}`));
16
17
  console.log();
17
- console.log(chalk.bold("Indexed libraries:"));
18
+ // Show breakdown by source
19
+ console.log(chalk.bold("By source:"));
20
+ for (const [sourceName, count] of Object.entries(stats.bySource)) {
21
+ console.log(` ${getSourceName(sourceName)}: ${count} items`);
22
+ }
23
+ console.log();
24
+ // Show breakdown by library
25
+ console.log(chalk.bold("By library:"));
18
26
  for (const [libName, count] of Object.entries(stats.byLibrary)) {
19
27
  const libConfig = getLibrary(libName);
20
- console.log(` ${chalk.green("ā—")} ${libName}: ${count} docs`);
28
+ console.log(` ${chalk.green("ā—")} ${libName}: ${count} items`);
21
29
  if (libConfig) {
22
30
  console.log(chalk.gray(` ${libConfig.description}`));
23
31
  }
@@ -34,4 +42,23 @@ export function statsCommand(options) {
34
42
  console.log();
35
43
  console.log(chalk.gray(`To add: pf-docs init --libs ${notIndexed[0].shortName}`));
36
44
  }
45
+ // Show examples/episodes status
46
+ const extrasNotIndexed = [];
47
+ if (!areExamplesCloned()) {
48
+ extrasNotIndexed.push("examples");
49
+ }
50
+ if (!areEpisodesCloned()) {
51
+ extrasNotIndexed.push("episodes");
52
+ }
53
+ if (extrasNotIndexed.length > 0) {
54
+ console.log(chalk.bold("Additional sources (not indexed):"));
55
+ if (extrasNotIndexed.includes("examples")) {
56
+ console.log(` ${chalk.gray("ā—‹")} examples — ${EXAMPLES_CONFIG.description}`);
57
+ }
58
+ if (extrasNotIndexed.includes("episodes")) {
59
+ console.log(` ${chalk.gray("ā—‹")} episodes — ${EPISODES_CONFIG.description}`);
60
+ }
61
+ console.log();
62
+ console.log(chalk.gray(`To add: pf-docs init --examples --episodes`));
63
+ }
37
64
  }
@@ -3,6 +3,8 @@
3
3
  */
4
4
  interface UpdateOptions {
5
5
  libs?: string[];
6
+ examples?: boolean;
7
+ episodes?: boolean;
6
8
  }
7
9
  export declare function updateCommand(options: UpdateOptions): Promise<void>;
8
10
  export {};
@@ -3,8 +3,8 @@
3
3
  */
4
4
  import chalk from "chalk";
5
5
  import { LIBRARIES, getLibrary } from "../config.js";
6
- import { updateLibrary, isLibraryCloned } from "../lib/repos.js";
7
- import { indexLibrary, openIndex, closeIndex } from "../lib/index.js";
6
+ import { updateLibrary, updateExamples, updateEpisodes, isLibraryCloned, areExamplesCloned, areEpisodesCloned } from "../lib/repos.js";
7
+ import { indexLibrary, indexExamples, indexEpisodes, openIndex, closeIndex } from "../lib/index.js";
8
8
  export async function updateCommand(options) {
9
9
  console.log(chalk.bold("\nšŸ”„ Updating Point-Free Documentation\n"));
10
10
  // Determine which libraries to update
@@ -24,27 +24,53 @@ export async function updateCommand(options) {
24
24
  }
25
25
  }
26
26
  }
27
- if (librariesToUpdate.length === 0) {
28
- console.log(chalk.yellow("No libraries to update. Run 'pf-docs init' first."));
27
+ // Check for examples/episodes
28
+ const shouldUpdateExamples = options.examples && areExamplesCloned();
29
+ const shouldUpdateEpisodes = options.episodes && areEpisodesCloned();
30
+ if (options.examples && !areExamplesCloned()) {
31
+ console.log(chalk.yellow(` ⚠ Examples not initialized. Run 'pf-docs init --examples' first.`));
32
+ }
33
+ if (options.episodes && !areEpisodesCloned()) {
34
+ console.log(chalk.yellow(` ⚠ Episodes not initialized. Run 'pf-docs init --episodes' first.`));
35
+ }
36
+ if (librariesToUpdate.length === 0 && !shouldUpdateExamples && !shouldUpdateEpisodes) {
37
+ console.log(chalk.yellow("Nothing to update. Run 'pf-docs init' first."));
29
38
  return;
30
39
  }
31
40
  // Update repositories
32
41
  console.log(chalk.bold("Pulling latest changes..."));
33
42
  const updatedLibs = [];
43
+ let examplesUpdated = false;
44
+ let episodesUpdated = false;
34
45
  for (const lib of librariesToUpdate) {
35
46
  const wasUpdated = await updateLibrary(lib);
36
47
  if (wasUpdated) {
37
48
  updatedLibs.push(lib);
38
49
  }
39
50
  }
40
- // Re-index updated libraries
41
- if (updatedLibs.length > 0) {
42
- console.log(chalk.bold("\nRe-indexing updated libraries..."));
51
+ if (shouldUpdateExamples) {
52
+ examplesUpdated = await updateExamples();
53
+ }
54
+ if (shouldUpdateEpisodes) {
55
+ episodesUpdated = await updateEpisodes();
56
+ }
57
+ // Re-index updated sources
58
+ const hasUpdates = updatedLibs.length > 0 || examplesUpdated || episodesUpdated;
59
+ if (hasUpdates) {
60
+ console.log(chalk.bold("\nRe-indexing updated sources..."));
43
61
  openIndex();
44
62
  for (const lib of updatedLibs) {
45
63
  const count = indexLibrary(lib);
46
64
  console.log(` āœ“ Re-indexed ${lib.shortName}: ${count} documents`);
47
65
  }
66
+ if (examplesUpdated) {
67
+ const count = indexExamples();
68
+ console.log(` āœ“ Re-indexed examples: ${count} files`);
69
+ }
70
+ if (episodesUpdated) {
71
+ const count = indexEpisodes();
72
+ console.log(` āœ“ Re-indexed episodes: ${count} files`);
73
+ }
48
74
  closeIndex();
49
75
  }
50
76
  console.log(chalk.green(`\nāœ… Update complete!\n`));
package/dist/config.d.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  /**
2
- * Configuration for Point-Free libraries
2
+ * Configuration for Point-Free libraries and example sources
3
3
  */
4
+ /**
5
+ * Source types for documentation
6
+ */
7
+ export type SourceType = "docs" | "examples" | "episodes";
4
8
  export interface LibraryConfig {
5
9
  name: string;
6
10
  shortName: string;
@@ -8,11 +12,34 @@ export interface LibraryConfig {
8
12
  docsPaths: string[];
9
13
  description: string;
10
14
  }
15
+ export interface ExamplesConfig {
16
+ name: string;
17
+ shortName: string;
18
+ repo: string;
19
+ paths: string[];
20
+ description: string;
21
+ filePatterns: string[];
22
+ }
23
+ export interface EpisodesConfig {
24
+ name: string;
25
+ shortName: string;
26
+ repo: string;
27
+ description: string;
28
+ filePatterns: string[];
29
+ }
11
30
  /**
12
31
  * All available Point-Free libraries
13
32
  * Add or remove libraries here to customize what's indexed
14
33
  */
15
34
  export declare const LIBRARIES: LibraryConfig[];
35
+ /**
36
+ * TCA Examples configuration (CaseStudies, SyncUps, etc.)
37
+ */
38
+ export declare const EXAMPLES_CONFIG: ExamplesConfig;
39
+ /**
40
+ * Episode code samples configuration
41
+ */
42
+ export declare const EPISODES_CONFIG: EpisodesConfig;
16
43
  /**
17
44
  * Get library config by short name
18
45
  */
@@ -21,6 +48,10 @@ export declare function getLibrary(shortName: string): LibraryConfig | undefined
21
48
  * All library short names
22
49
  */
23
50
  export declare const LIBRARY_NAMES: string[];
51
+ /**
52
+ * All valid source types
53
+ */
54
+ export declare const SOURCE_TYPES: SourceType[];
24
55
  export declare const PATHS: {
25
56
  dataDir: string;
26
57
  reposDir: string;
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Configuration for Point-Free libraries
2
+ * Configuration for Point-Free libraries and example sources
3
3
  */
4
4
  import { join } from "path";
5
5
  /**
@@ -100,6 +100,35 @@ export const LIBRARIES = [
100
100
  description: "Runtime warnings & assertions",
101
101
  },
102
102
  ];
103
+ /**
104
+ * TCA Examples configuration (CaseStudies, SyncUps, etc.)
105
+ */
106
+ export const EXAMPLES_CONFIG = {
107
+ name: "tca-examples",
108
+ shortName: "examples",
109
+ repo: "pointfreeco/swift-composable-architecture",
110
+ paths: [
111
+ "Examples/CaseStudies",
112
+ "Examples/SyncUps",
113
+ "Examples/Todos",
114
+ "Examples/VoiceMemos",
115
+ "Examples/Search",
116
+ "Examples/TicTacToe",
117
+ "Examples/SpeechRecognition",
118
+ ],
119
+ description: "TCA example apps and case studies",
120
+ filePatterns: ["**/*.swift"],
121
+ };
122
+ /**
123
+ * Episode code samples configuration
124
+ */
125
+ export const EPISODES_CONFIG = {
126
+ name: "episode-code-samples",
127
+ shortName: "episodes",
128
+ repo: "pointfreeco/episode-code-samples",
129
+ description: "Point-Free episode code samples (350+ episodes)",
130
+ filePatterns: ["**/*.swift", "**/*.playground/**/*.swift"],
131
+ };
103
132
  /**
104
133
  * Get library config by short name
105
134
  */
@@ -110,6 +139,10 @@ export function getLibrary(shortName) {
110
139
  * All library short names
111
140
  */
112
141
  export const LIBRARY_NAMES = LIBRARIES.map((lib) => lib.shortName);
142
+ /**
143
+ * All valid source types
144
+ */
145
+ export const SOURCE_TYPES = ["docs", "examples", "episodes"];
113
146
  /**
114
147
  * Paths configuration
115
148
  */
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Shared formatting utilities
3
+ */
4
+ import { SourceType } from "../config.js";
5
+ /**
6
+ * Get source type label with color and brackets for search/list results
7
+ */
8
+ export declare function getSourceLabel(source: SourceType): string;
9
+ /**
10
+ * Get source type name with color (for stats display)
11
+ */
12
+ export declare function getSourceName(source: SourceType): string;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Shared formatting utilities
3
+ */
4
+ import chalk from "chalk";
5
+ /**
6
+ * Get source type label with color and brackets for search/list results
7
+ */
8
+ export function getSourceLabel(source) {
9
+ switch (source) {
10
+ case "docs":
11
+ return chalk.cyan("[DOC]");
12
+ case "examples":
13
+ return chalk.magenta("[EXAMPLE]");
14
+ case "episodes":
15
+ return chalk.yellow("[EPISODE]");
16
+ }
17
+ }
18
+ /**
19
+ * Get source type name with color (for stats display)
20
+ */
21
+ export function getSourceName(source) {
22
+ switch (source) {
23
+ case "docs":
24
+ return chalk.cyan("docs");
25
+ case "examples":
26
+ return chalk.magenta("examples");
27
+ case "episodes":
28
+ return chalk.yellow("episodes");
29
+ }
30
+ }
@@ -2,11 +2,12 @@
2
2
  * Search index using SQLite FTS5 for full-text search
3
3
  */
4
4
  import Database from "better-sqlite3";
5
- import { LibraryConfig } from "../config.js";
5
+ import { LibraryConfig, SourceType } from "../config.js";
6
6
  export interface DocEntry {
7
7
  library: string;
8
8
  path: string;
9
9
  title: string;
10
+ source: SourceType;
10
11
  }
11
12
  export interface DocWithContent extends DocEntry {
12
13
  content: string;
@@ -14,6 +15,7 @@ export interface DocWithContent extends DocEntry {
14
15
  export interface IndexStats {
15
16
  totalDocs: number;
16
17
  byLibrary: Record<string, number>;
18
+ bySource: Record<SourceType, number>;
17
19
  }
18
20
  /**
19
21
  * Initialize or open the search index database
@@ -23,6 +25,14 @@ export declare function openIndex(): Database.Database;
23
25
  * Index all documentation files for a library
24
26
  */
25
27
  export declare function indexLibrary(lib: LibraryConfig): number;
28
+ /**
29
+ * Index TCA examples (CaseStudies, etc.)
30
+ */
31
+ export declare function indexExamples(): number;
32
+ /**
33
+ * Index episode code samples
34
+ */
35
+ export declare function indexEpisodes(): number;
26
36
  /**
27
37
  * Search the index
28
38
  */
@@ -32,15 +42,17 @@ export interface SearchResult {
32
42
  title: string;
33
43
  snippet: string;
34
44
  score: number;
45
+ source: SourceType;
35
46
  }
36
47
  export declare function search(query: string, options?: {
37
48
  lib?: string;
38
49
  limit?: number;
50
+ source?: SourceType | "all";
39
51
  }): SearchResult[];
40
52
  /**
41
53
  * List all indexed documents
42
54
  */
43
- export declare function listDocs(lib?: string): DocEntry[];
55
+ export declare function listDocs(lib?: string, source?: SourceType | "all"): DocEntry[];
44
56
  /**
45
57
  * Get a specific document by path
46
58
  */