@larkiny/astro-github-loader 0.11.3 โ†’ 0.12.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.
Files changed (51) hide show
  1. package/README.md +28 -55
  2. package/dist/github.assets.d.ts +70 -0
  3. package/dist/github.assets.js +253 -0
  4. package/dist/github.auth.js +13 -9
  5. package/dist/github.cleanup.d.ts +3 -2
  6. package/dist/github.cleanup.js +30 -23
  7. package/dist/github.constants.d.ts +0 -16
  8. package/dist/github.constants.js +0 -16
  9. package/dist/github.content.d.ts +5 -131
  10. package/dist/github.content.js +152 -794
  11. package/dist/github.dryrun.d.ts +9 -5
  12. package/dist/github.dryrun.js +46 -25
  13. package/dist/github.link-transform.d.ts +2 -2
  14. package/dist/github.link-transform.js +65 -57
  15. package/dist/github.loader.js +30 -46
  16. package/dist/github.logger.d.ts +2 -2
  17. package/dist/github.logger.js +33 -24
  18. package/dist/github.paths.d.ts +76 -0
  19. package/dist/github.paths.js +190 -0
  20. package/dist/github.storage.d.ts +15 -0
  21. package/dist/github.storage.js +109 -0
  22. package/dist/github.types.d.ts +34 -4
  23. package/dist/index.d.ts +8 -6
  24. package/dist/index.js +3 -6
  25. package/dist/test-helpers.d.ts +130 -0
  26. package/dist/test-helpers.js +194 -0
  27. package/package.json +3 -1
  28. package/src/github.assets.spec.ts +717 -0
  29. package/src/github.assets.ts +365 -0
  30. package/src/github.auth.spec.ts +245 -0
  31. package/src/github.auth.ts +24 -10
  32. package/src/github.cleanup.spec.ts +380 -0
  33. package/src/github.cleanup.ts +91 -47
  34. package/src/github.constants.ts +0 -17
  35. package/src/github.content.spec.ts +305 -454
  36. package/src/github.content.ts +259 -957
  37. package/src/github.dryrun.spec.ts +586 -0
  38. package/src/github.dryrun.ts +105 -54
  39. package/src/github.link-transform.spec.ts +1345 -0
  40. package/src/github.link-transform.ts +174 -95
  41. package/src/github.loader.spec.ts +75 -50
  42. package/src/github.loader.ts +101 -76
  43. package/src/github.logger.spec.ts +795 -0
  44. package/src/github.logger.ts +77 -35
  45. package/src/github.paths.spec.ts +523 -0
  46. package/src/github.paths.ts +259 -0
  47. package/src/github.storage.spec.ts +367 -0
  48. package/src/github.storage.ts +127 -0
  49. package/src/github.types.ts +48 -9
  50. package/src/index.ts +43 -6
  51. package/src/test-helpers.ts +215 -0
@@ -1,48 +1,26 @@
1
1
  import { toCollectionEntry } from "./github.content.js";
2
2
  import { performSelectiveCleanup } from "./github.cleanup.js";
3
- import { performDryRun, displayDryRunResults, updateImportState, loadImportState, createConfigId, getLatestCommitInfo } from "./github.dryrun.js";
4
- import { createLogger, type Logger, type ImportSummary } from "./github.logger.js";
3
+ import {
4
+ performDryRun,
5
+ displayDryRunResults,
6
+ updateImportState,
7
+ loadImportState,
8
+ createConfigId,
9
+ getLatestCommitInfo,
10
+ } from "./github.dryrun.js";
11
+ import {
12
+ createLogger,
13
+ type Logger,
14
+ type ImportSummary,
15
+ } from "./github.logger.js";
5
16
 
6
17
  import type {
7
18
  Loader,
8
19
  GithubLoaderOptions,
20
+ ExtendedLoaderContext,
9
21
  ImportOptions,
10
- SyncStats,
11
22
  } from "./github.types.js";
12
23
 
13
- /**
14
- * Performs selective cleanup for configurations with basePath
15
- * @param configs - Array of configuration objects
16
- * @param context - Loader context
17
- * @param octokit - GitHub API client
18
- * @internal
19
- */
20
- async function performSelectiveCleanups(
21
- configs: ImportOptions[],
22
- context: any,
23
- octokit: any
24
- ): Promise<SyncStats[]> {
25
- const results: SyncStats[] = [];
26
-
27
- // Process each config sequentially to avoid overwhelming Astro's file watcher
28
- for (const config of configs) {
29
- if (config.enabled === false) {
30
- context.logger.debug(`Skipping disabled config: ${config.name || `${config.owner}/${config.repo}`}`);
31
- continue;
32
- }
33
-
34
- try {
35
- const stats = await performSelectiveCleanup(config, context, octokit);
36
- results.push(stats);
37
- } catch (error: any) {
38
- context.logger.error(`Selective cleanup failed for ${config.name || `${config.owner}/${config.repo}`}: ${error}`);
39
- // Continue with other configs even if one fails
40
- }
41
- }
42
-
43
- return results;
44
- }
45
-
46
24
  /**
47
25
  * Loads data from GitHub repositories based on the provided configurations and options.
48
26
  *
@@ -67,12 +45,13 @@ export function githubLoader({
67
45
  return {
68
46
  name: "github-loader",
69
47
  load: async (context) => {
70
-
71
48
  // Create global logger with specified level or default
72
- const globalLogger = createLogger(logLevel || 'default');
49
+ const globalLogger = createLogger(logLevel || "default");
73
50
 
74
51
  if (dryRun) {
75
- globalLogger.info("๐Ÿ” Dry run mode enabled - checking for changes only");
52
+ globalLogger.info(
53
+ "๐Ÿ” Dry run mode enabled - checking for changes only",
54
+ );
76
55
 
77
56
  try {
78
57
  const results = await performDryRun(configs, context, octokit);
@@ -82,8 +61,10 @@ export function githubLoader({
82
61
  globalLogger.info("๐Ÿ’ก Set dryRun: false to perform actual imports");
83
62
 
84
63
  return; // Exit without importing
85
- } catch (error: any) {
86
- globalLogger.error(`Dry run failed: ${error.message}`);
64
+ } catch (error: unknown) {
65
+ globalLogger.error(
66
+ `Dry run failed: ${error instanceof Error ? error.message : String(error)}`,
67
+ );
87
68
  throw error;
88
69
  }
89
70
  }
@@ -92,27 +73,36 @@ export function githubLoader({
92
73
 
93
74
  // Log clear mode status - actual clearing happens per-entry in toCollectionEntry
94
75
  // to avoid breaking Astro's content collection by emptying the store all at once
95
- globalLogger.info(clear ? "Processing with selective entry replacement" : "Processing without entry replacement");
76
+ globalLogger.info(
77
+ clear
78
+ ? "Processing with selective entry replacement"
79
+ : "Processing without entry replacement",
80
+ );
96
81
 
97
82
  // Process each config sequentially to avoid overwhelming GitHub API/CDN
98
83
  for (let i = 0; i < configs.length; i++) {
99
84
  const config = configs[i];
100
85
 
101
86
  if (config.enabled === false) {
102
- globalLogger.debug(`Skipping disabled config: ${config.name || `${config.owner}/${config.repo}`}`);
87
+ globalLogger.debug(
88
+ `Skipping disabled config: ${config.name || `${config.owner}/${config.repo}`}`,
89
+ );
103
90
  continue;
104
91
  }
105
92
 
106
93
  // Add small delay between configs to be gentler on GitHub's CDN
107
94
  if (i > 0) {
108
- await new Promise(resolve => setTimeout(resolve, 1000));
95
+ await new Promise((resolve) => setTimeout(resolve, 1000));
109
96
  }
110
97
 
111
98
  // Determine the effective log level for this config
112
- const effectiveLogLevel = logLevel || config.logLevel || 'default';
99
+ const effectiveLogLevel = logLevel || config.logLevel || "default";
113
100
  const configLogger = createLogger(effectiveLogLevel);
114
101
 
115
- const configName = config.name || `${config.owner}/${config.repo}`;
102
+ const langSuffix = config.language ? ` (${config.language})` : "";
103
+ const configName = config.name
104
+ ? `${config.name}${langSuffix}`
105
+ : `${config.owner}/${config.repo}${langSuffix}`;
116
106
  const repository = `${config.owner}/${config.repo}`;
117
107
 
118
108
  let summary: ImportSummary = {
@@ -123,7 +113,7 @@ export function githubLoader({
123
113
  filesUpdated: 0,
124
114
  filesUnchanged: 0,
125
115
  duration: 0,
126
- status: 'error',
116
+ status: "error",
127
117
  };
128
118
 
129
119
  const startTime = Date.now();
@@ -134,37 +124,52 @@ export function githubLoader({
134
124
 
135
125
  if (!force) {
136
126
  try {
137
- const state = await loadImportState(process.cwd());
127
+ const state = await loadImportState(process.cwd(), configLogger);
138
128
  const currentState = state.imports[configId];
139
129
 
140
130
  if (currentState && currentState.lastCommitSha) {
141
- configLogger.debug(`๐Ÿ” Checking repository changes for ${configName}...`);
131
+ configLogger.debug(
132
+ `๐Ÿ” Checking repository changes for ${configName}...`,
133
+ );
142
134
  const latestCommit = await getLatestCommitInfo(octokit, config);
143
135
 
144
- if (latestCommit && currentState.lastCommitSha === latestCommit.sha) {
145
- configLogger.info(`โœ… Repository ${configName} unchanged (${latestCommit.sha.slice(0, 7)}) - skipping import`);
136
+ if (
137
+ latestCommit &&
138
+ currentState.lastCommitSha === latestCommit.sha
139
+ ) {
140
+ configLogger.info(
141
+ `โœ… Repository ${configName} unchanged (${latestCommit.sha.slice(0, 7)}) - skipping import`,
142
+ );
146
143
 
147
144
  // Update summary for unchanged repository
148
145
  summary.duration = Date.now() - startTime;
149
146
  summary.filesProcessed = 0;
150
147
  summary.filesUpdated = 0;
151
148
  summary.filesUnchanged = 0;
152
- summary.status = 'success';
149
+ summary.status = "success";
153
150
 
154
151
  configLogger.logImportSummary(summary);
155
152
  continue; // Skip to next config
156
153
  } else if (latestCommit) {
157
- configLogger.info(`๐Ÿ”„ Repository ${configName} changed (${currentState.lastCommitSha?.slice(0, 7) || 'unknown'} -> ${latestCommit.sha.slice(0, 7)}) - proceeding with import`);
154
+ configLogger.info(
155
+ `๐Ÿ”„ Repository ${configName} changed (${currentState.lastCommitSha?.slice(0, 7) || "unknown"} -> ${latestCommit.sha.slice(0, 7)}) - proceeding with import`,
156
+ );
158
157
  }
159
158
  } else {
160
- configLogger.debug(`๐Ÿ“ฅ First time importing ${configName} - no previous state found`);
159
+ configLogger.debug(
160
+ `๐Ÿ“ฅ First time importing ${configName} - no previous state found`,
161
+ );
161
162
  }
162
163
  } catch (error) {
163
- configLogger.warn(`Failed to check repository state for ${configName}: ${error instanceof Error ? error.message : String(error)}`);
164
+ configLogger.warn(
165
+ `Failed to check repository state for ${configName}: ${error instanceof Error ? error.message : String(error)}`,
166
+ );
164
167
  // Continue with import if state check fails
165
168
  }
166
169
  } else {
167
- configLogger.info(`๐Ÿ”„ Force mode enabled for ${configName} - proceeding with full import`);
170
+ configLogger.info(
171
+ `๐Ÿ”„ Force mode enabled for ${configName} - proceeding with full import`,
172
+ );
168
173
  }
169
174
 
170
175
  // Determine effective clear setting: per-config takes precedence over global
@@ -172,27 +177,39 @@ export function githubLoader({
172
177
 
173
178
  // Perform selective cleanup before importing if clear is enabled
174
179
  if (effectiveClear) {
175
- configLogger.info(`๐Ÿงน Clearing obsolete files for ${configName}...`);
180
+ configLogger.info(
181
+ `๐Ÿงน Clearing obsolete files for ${configName}...`,
182
+ );
176
183
  try {
177
- await performSelectiveCleanup(config, { ...context, logger: configLogger as any }, octokit);
184
+ await performSelectiveCleanup(
185
+ config,
186
+ { ...context, logger: configLogger } as ExtendedLoaderContext,
187
+ octokit,
188
+ );
178
189
  } catch (error) {
179
- configLogger.warn(`Cleanup failed for ${configName}, continuing with import: ${error}`);
190
+ configLogger.warn(
191
+ `Cleanup failed for ${configName}, continuing with import: ${error}`,
192
+ );
180
193
  }
181
194
  }
182
195
 
183
196
  // Perform the import with spinner
184
197
  const stats = await globalLogger.withSpinner(
185
198
  `๐Ÿ”„ Importing ${configName}...`,
186
- () => toCollectionEntry({
187
- context: { ...context, logger: configLogger as any },
188
- octokit,
189
- options: config,
190
- fetchOptions,
191
- force,
192
- clear: effectiveClear,
193
- }),
199
+ () =>
200
+ toCollectionEntry({
201
+ context: {
202
+ ...context,
203
+ logger: configLogger,
204
+ } as ExtendedLoaderContext,
205
+ octokit,
206
+ options: config,
207
+ fetchOptions,
208
+ force,
209
+ clear: effectiveClear,
210
+ }),
194
211
  `โœ… ${configName} imported successfully`,
195
- `โŒ ${configName} import failed`
212
+ `โŒ ${configName} import failed`,
196
213
  );
197
214
 
198
215
  summary.duration = Date.now() - startTime;
@@ -201,7 +218,7 @@ export function githubLoader({
201
218
  summary.filesUnchanged = stats?.unchanged || 0;
202
219
  summary.assetsDownloaded = stats?.assetsDownloaded || 0;
203
220
  summary.assetsCached = stats?.assetsCached || 0;
204
- summary.status = 'success';
221
+ summary.status = "success";
205
222
 
206
223
  // Log structured summary
207
224
  configLogger.logImportSummary(summary);
@@ -212,21 +229,29 @@ export function githubLoader({
212
229
  const { data } = await octokit.rest.repos.listCommits({
213
230
  owner: config.owner,
214
231
  repo: config.repo,
215
- sha: config.ref || 'main',
216
- per_page: 1
232
+ sha: config.ref || "main",
233
+ per_page: 1,
217
234
  });
218
235
 
219
236
  if (data.length > 0) {
220
- await updateImportState(process.cwd(), config, data[0].sha);
237
+ await updateImportState(
238
+ process.cwd(),
239
+ config,
240
+ data[0].sha,
241
+ configLogger,
242
+ );
221
243
  }
222
244
  } catch (error) {
223
245
  // Don't fail the import if state tracking fails
224
- configLogger.debug(`Failed to update import state for ${configName}: ${error}`);
246
+ configLogger.debug(
247
+ `Failed to update import state for ${configName}: ${error}`,
248
+ );
225
249
  }
226
- } catch (error: any) {
250
+ } catch (error: unknown) {
227
251
  summary.duration = Date.now() - startTime;
228
- summary.status = 'error';
229
- summary.error = error.message;
252
+ summary.status = "error";
253
+ summary.error =
254
+ error instanceof Error ? error.message : String(error);
230
255
 
231
256
  configLogger.logImportSummary(summary);
232
257
  // Continue with other configs even if one fails