@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.
- package/README.md +28 -55
- package/dist/github.assets.d.ts +70 -0
- package/dist/github.assets.js +253 -0
- package/dist/github.auth.js +13 -9
- package/dist/github.cleanup.d.ts +3 -2
- package/dist/github.cleanup.js +30 -23
- package/dist/github.constants.d.ts +0 -16
- package/dist/github.constants.js +0 -16
- package/dist/github.content.d.ts +5 -131
- package/dist/github.content.js +152 -794
- package/dist/github.dryrun.d.ts +9 -5
- package/dist/github.dryrun.js +46 -25
- package/dist/github.link-transform.d.ts +2 -2
- package/dist/github.link-transform.js +65 -57
- package/dist/github.loader.js +30 -46
- package/dist/github.logger.d.ts +2 -2
- package/dist/github.logger.js +33 -24
- package/dist/github.paths.d.ts +76 -0
- package/dist/github.paths.js +190 -0
- package/dist/github.storage.d.ts +15 -0
- package/dist/github.storage.js +109 -0
- package/dist/github.types.d.ts +34 -4
- package/dist/index.d.ts +8 -6
- package/dist/index.js +3 -6
- package/dist/test-helpers.d.ts +130 -0
- package/dist/test-helpers.js +194 -0
- package/package.json +3 -1
- package/src/github.assets.spec.ts +717 -0
- package/src/github.assets.ts +365 -0
- package/src/github.auth.spec.ts +245 -0
- package/src/github.auth.ts +24 -10
- package/src/github.cleanup.spec.ts +380 -0
- package/src/github.cleanup.ts +91 -47
- package/src/github.constants.ts +0 -17
- package/src/github.content.spec.ts +305 -454
- package/src/github.content.ts +259 -957
- package/src/github.dryrun.spec.ts +586 -0
- package/src/github.dryrun.ts +105 -54
- package/src/github.link-transform.spec.ts +1345 -0
- package/src/github.link-transform.ts +174 -95
- package/src/github.loader.spec.ts +75 -50
- package/src/github.loader.ts +101 -76
- package/src/github.logger.spec.ts +795 -0
- package/src/github.logger.ts +77 -35
- package/src/github.paths.spec.ts +523 -0
- package/src/github.paths.ts +259 -0
- package/src/github.storage.spec.ts +367 -0
- package/src/github.storage.ts +127 -0
- package/src/github.types.ts +48 -9
- package/src/index.ts +43 -6
- package/src/test-helpers.ts +215 -0
package/dist/github.cleanup.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { generateId, generatePath, shouldIncludeFile } from "./github.content.js";
|
|
4
|
+
import { generateId, generatePath, shouldIncludeFile, } from "./github.content.js";
|
|
5
5
|
const SLEEP_BETWEEN_DELETES = 10; // ms between file deletions
|
|
6
6
|
/**
|
|
7
7
|
* Sleep utility for pacing file operations
|
|
8
8
|
*/
|
|
9
9
|
function sleep(ms) {
|
|
10
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
10
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
13
|
* Gets all files that should exist locally based on remote repository state
|
|
14
14
|
*/
|
|
15
|
-
async function getExpectedFiles(octokit, options, signal) {
|
|
15
|
+
async function getExpectedFiles(octokit, options, logger, signal) {
|
|
16
16
|
const { owner, repo, ref = "main" } = options;
|
|
17
17
|
const expectedFiles = new Set();
|
|
18
18
|
// Get all unique directory prefixes from include patterns to limit scanning
|
|
@@ -22,13 +22,15 @@ async function getExpectedFiles(octokit, options, signal) {
|
|
|
22
22
|
// Extract directory part from pattern (before any glob wildcards)
|
|
23
23
|
const pattern = includePattern.pattern;
|
|
24
24
|
const beforeGlob = pattern.split(/[*?{]/)[0];
|
|
25
|
-
const dirPart = beforeGlob.includes(
|
|
25
|
+
const dirPart = beforeGlob.includes("/")
|
|
26
|
+
? beforeGlob.substring(0, beforeGlob.lastIndexOf("/"))
|
|
27
|
+
: "";
|
|
26
28
|
directoriesToScan.add(dirPart);
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
else {
|
|
30
32
|
// If no includes specified, scan from root
|
|
31
|
-
directoriesToScan.add(
|
|
33
|
+
directoriesToScan.add("");
|
|
32
34
|
}
|
|
33
35
|
async function processDirectory(dirPath) {
|
|
34
36
|
try {
|
|
@@ -37,16 +39,19 @@ async function getExpectedFiles(octokit, options, signal) {
|
|
|
37
39
|
repo,
|
|
38
40
|
path: dirPath,
|
|
39
41
|
ref,
|
|
40
|
-
request: { signal }
|
|
42
|
+
request: { signal },
|
|
41
43
|
});
|
|
42
44
|
if (!Array.isArray(data)) {
|
|
43
45
|
// Single file
|
|
44
|
-
if (data.type ===
|
|
46
|
+
if (data.type === "file" &&
|
|
47
|
+
shouldIncludeFile(data.path, options).included) {
|
|
45
48
|
const id = generateId(data.path);
|
|
46
49
|
const includeResult = shouldIncludeFile(data.path, options);
|
|
47
50
|
const localPath = generatePath(data.path, includeResult.included ? includeResult.matchedPattern : null, options);
|
|
48
51
|
// Convert to absolute path for consistent comparison
|
|
49
|
-
const absolutePath = localPath.startsWith(
|
|
52
|
+
const absolutePath = localPath.startsWith("/")
|
|
53
|
+
? localPath
|
|
54
|
+
: join(process.cwd(), localPath);
|
|
50
55
|
expectedFiles.add(absolutePath);
|
|
51
56
|
}
|
|
52
57
|
return;
|
|
@@ -69,7 +74,9 @@ async function getExpectedFiles(octokit, options, signal) {
|
|
|
69
74
|
const includeResult = shouldIncludeFile(itemPath, options);
|
|
70
75
|
const localPath = generatePath(itemPath, includeResult.included ? includeResult.matchedPattern : null, options);
|
|
71
76
|
// Convert to absolute path for consistent comparison
|
|
72
|
-
const absolutePath = localPath.startsWith(
|
|
77
|
+
const absolutePath = localPath.startsWith("/")
|
|
78
|
+
? localPath
|
|
79
|
+
: join(process.cwd(), localPath);
|
|
73
80
|
expectedFiles.add(absolutePath);
|
|
74
81
|
}
|
|
75
82
|
});
|
|
@@ -78,7 +85,7 @@ async function getExpectedFiles(octokit, options, signal) {
|
|
|
78
85
|
catch (error) {
|
|
79
86
|
if (signal?.aborted)
|
|
80
87
|
throw error;
|
|
81
|
-
|
|
88
|
+
logger.warn(`Failed to process directory ${dirPath}: ${error}`);
|
|
82
89
|
}
|
|
83
90
|
}
|
|
84
91
|
// Process only the directories that match our include patterns
|
|
@@ -90,7 +97,7 @@ async function getExpectedFiles(octokit, options, signal) {
|
|
|
90
97
|
/**
|
|
91
98
|
* Gets all existing local files in the basePath as absolute paths
|
|
92
99
|
*/
|
|
93
|
-
async function getExistingFiles(basePath) {
|
|
100
|
+
async function getExistingFiles(basePath, logger) {
|
|
94
101
|
const existingFiles = new Set();
|
|
95
102
|
if (!existsSync(basePath)) {
|
|
96
103
|
return existingFiles;
|
|
@@ -102,20 +109,20 @@ async function getExistingFiles(basePath) {
|
|
|
102
109
|
const fullPath = join(dirPath, entry.name);
|
|
103
110
|
if (entry.isDirectory()) {
|
|
104
111
|
// Skip manifest files and other system directories
|
|
105
|
-
if (!entry.name.startsWith(
|
|
112
|
+
if (!entry.name.startsWith(".")) {
|
|
106
113
|
await walkDirectory(fullPath);
|
|
107
114
|
}
|
|
108
115
|
}
|
|
109
116
|
else if (entry.isFile()) {
|
|
110
117
|
// Skip manifest and system files
|
|
111
|
-
if (!entry.name.startsWith(
|
|
118
|
+
if (!entry.name.startsWith(".")) {
|
|
112
119
|
existingFiles.add(fullPath);
|
|
113
120
|
}
|
|
114
121
|
}
|
|
115
122
|
}
|
|
116
123
|
}
|
|
117
124
|
catch (error) {
|
|
118
|
-
|
|
125
|
+
logger.warn(`Failed to read directory ${dirPath}: ${error}`);
|
|
119
126
|
}
|
|
120
127
|
}
|
|
121
128
|
await walkDirectory(basePath);
|
|
@@ -135,7 +142,7 @@ export async function performSelectiveCleanup(config, context, octokit, signal)
|
|
|
135
142
|
updated: 0,
|
|
136
143
|
deleted: 0,
|
|
137
144
|
unchanged: 0,
|
|
138
|
-
duration: Date.now() - startTime
|
|
145
|
+
duration: Date.now() - startTime,
|
|
139
146
|
};
|
|
140
147
|
}
|
|
141
148
|
logger.debug(`Starting selective cleanup for ${configName}`);
|
|
@@ -143,8 +150,8 @@ export async function performSelectiveCleanup(config, context, octokit, signal)
|
|
|
143
150
|
// Get existing local files from all include pattern base paths
|
|
144
151
|
const allExistingFiles = new Set();
|
|
145
152
|
for (const includePattern of config.includes) {
|
|
146
|
-
const existingFiles = await getExistingFiles(includePattern.basePath);
|
|
147
|
-
existingFiles.forEach(file => allExistingFiles.add(file));
|
|
153
|
+
const existingFiles = await getExistingFiles(includePattern.basePath, logger);
|
|
154
|
+
existingFiles.forEach((file) => allExistingFiles.add(file));
|
|
148
155
|
}
|
|
149
156
|
// If no existing files, skip cleanup (fresh import)
|
|
150
157
|
if (allExistingFiles.size === 0) {
|
|
@@ -154,11 +161,11 @@ export async function performSelectiveCleanup(config, context, octokit, signal)
|
|
|
154
161
|
updated: 0,
|
|
155
162
|
deleted: 0,
|
|
156
163
|
unchanged: 0,
|
|
157
|
-
duration: Date.now() - startTime
|
|
164
|
+
duration: Date.now() - startTime,
|
|
158
165
|
};
|
|
159
166
|
}
|
|
160
167
|
// Get expected files from remote repository
|
|
161
|
-
const expectedFiles = await getExpectedFiles(octokit, config, signal);
|
|
168
|
+
const expectedFiles = await getExpectedFiles(octokit, config, logger, signal);
|
|
162
169
|
// Find files to delete (exist locally but not in remote)
|
|
163
170
|
const filesToDelete = [];
|
|
164
171
|
for (const existingFile of allExistingFiles) {
|
|
@@ -184,10 +191,10 @@ export async function performSelectiveCleanup(config, context, octokit, signal)
|
|
|
184
191
|
const duration = Date.now() - startTime;
|
|
185
192
|
const stats = {
|
|
186
193
|
added: 0, // Will be counted by main sync process
|
|
187
|
-
updated: 0, // Will be counted by main sync process
|
|
194
|
+
updated: 0, // Will be counted by main sync process
|
|
188
195
|
deleted: deletedCount,
|
|
189
196
|
unchanged: 0, // Will be counted by main sync process
|
|
190
|
-
duration
|
|
197
|
+
duration,
|
|
191
198
|
};
|
|
192
199
|
if (deletedCount > 0) {
|
|
193
200
|
logger.info(`Cleanup completed for ${configName}: ${deletedCount} obsolete files deleted (${duration}ms)`);
|
|
@@ -203,14 +210,14 @@ export async function performSelectiveCleanup(config, context, octokit, signal)
|
|
|
203
210
|
throw error;
|
|
204
211
|
}
|
|
205
212
|
const duration = Date.now() - startTime;
|
|
206
|
-
logger.error(`Cleanup failed for ${configName} after ${duration}ms: ${error}`);
|
|
213
|
+
logger.error(`Cleanup failed for ${configName} after ${duration}ms: ${error instanceof Error ? error.message : String(error)}`);
|
|
207
214
|
// Don't throw - let the main sync process continue
|
|
208
215
|
return {
|
|
209
216
|
added: 0,
|
|
210
217
|
updated: 0,
|
|
211
218
|
deleted: 0,
|
|
212
219
|
unchanged: 0,
|
|
213
|
-
duration
|
|
220
|
+
duration,
|
|
214
221
|
};
|
|
215
222
|
}
|
|
216
223
|
}
|
|
@@ -6,19 +6,3 @@
|
|
|
6
6
|
* @internal
|
|
7
7
|
*/
|
|
8
8
|
export declare const INVALID_STRING_ERROR = "Invalid string";
|
|
9
|
-
/**
|
|
10
|
-
* Represents an error message indicating that a provided URL is invalid.
|
|
11
|
-
* This constant is typically used for validation or error handling when a URL
|
|
12
|
-
* does not conform to the expected format or requirements.
|
|
13
|
-
*
|
|
14
|
-
* @internal
|
|
15
|
-
*/
|
|
16
|
-
export declare const INVALID_URL_ERROR = "Invalid url";
|
|
17
|
-
/**
|
|
18
|
-
* A constant that holds a default error message indicating that a service response is invalid.
|
|
19
|
-
* This value is typically used to signify that the response from a service or API call
|
|
20
|
-
* does not meet the expected format, structure, or criteria.
|
|
21
|
-
*
|
|
22
|
-
* @internal
|
|
23
|
-
*/
|
|
24
|
-
export declare const INVALID_SERVICE_RESPONSE = "Invalid service response";
|
package/dist/github.constants.js
CHANGED
|
@@ -6,19 +6,3 @@
|
|
|
6
6
|
* @internal
|
|
7
7
|
*/
|
|
8
8
|
export const INVALID_STRING_ERROR = "Invalid string";
|
|
9
|
-
/**
|
|
10
|
-
* Represents an error message indicating that a provided URL is invalid.
|
|
11
|
-
* This constant is typically used for validation or error handling when a URL
|
|
12
|
-
* does not conform to the expected format or requirements.
|
|
13
|
-
*
|
|
14
|
-
* @internal
|
|
15
|
-
*/
|
|
16
|
-
export const INVALID_URL_ERROR = "Invalid url";
|
|
17
|
-
/**
|
|
18
|
-
* A constant that holds a default error message indicating that a service response is invalid.
|
|
19
|
-
* This value is typically used to signify that the response from a service or API call
|
|
20
|
-
* does not meet the expected format, structure, or criteria.
|
|
21
|
-
*
|
|
22
|
-
* @internal
|
|
23
|
-
*/
|
|
24
|
-
export const INVALID_SERVICE_RESPONSE = "Invalid service response";
|
package/dist/github.content.d.ts
CHANGED
|
@@ -1,110 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
assetsDownloaded?: number;
|
|
7
|
-
assetsCached?: number;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Generates a unique identifier from a file path by removing the extension
|
|
11
|
-
* @param filePath - The file path to generate ID from
|
|
12
|
-
* @return {string} The generated identifier as a string with extension removed
|
|
13
|
-
* @internal
|
|
14
|
-
*/
|
|
15
|
-
export declare function generateId(filePath: string): string;
|
|
16
|
-
/**
|
|
17
|
-
* Applies path mapping logic to get the final filename for a file
|
|
18
|
-
*
|
|
19
|
-
* Supports two types of path mappings:
|
|
20
|
-
* - **File mapping**: Exact file path match (e.g., 'docs/README.md' -> 'docs/overview.md')
|
|
21
|
-
* - **Folder mapping**: Folder path with trailing slash (e.g., 'docs/capabilities/' -> 'docs/')
|
|
22
|
-
*
|
|
23
|
-
* @param filePath - Original source file path
|
|
24
|
-
* @param matchedPattern - The pattern that matched this file
|
|
25
|
-
* @param options - Import options containing path mappings
|
|
26
|
-
* @returns Final filename after applying path mapping logic
|
|
27
|
-
* @internal
|
|
28
|
-
*/
|
|
29
|
-
export declare function applyRename(filePath: string, matchedPattern?: MatchedPattern | null, options?: ImportOptions): string;
|
|
30
|
-
/**
|
|
31
|
-
* Generates a local file path based on the matched pattern and file path
|
|
32
|
-
* @param filePath - The original file path from the repository
|
|
33
|
-
* @param matchedPattern - The pattern that matched this file (or null if no includes specified)
|
|
34
|
-
* @param options - Import options containing includes patterns for path mapping lookups
|
|
35
|
-
* @return {string} The local file path where this content should be stored
|
|
36
|
-
* @internal
|
|
37
|
-
*/
|
|
38
|
-
export declare function generatePath(filePath: string, matchedPattern?: MatchedPattern | null, options?: ImportOptions): string;
|
|
39
|
-
/**
|
|
40
|
-
* Synchronizes a file by ensuring the target directory exists and then writing the specified content to the file at the given path.
|
|
41
|
-
*
|
|
42
|
-
* @param {string} path - The path of the file to synchronize, including its directory and filename.
|
|
43
|
-
* @param {string} content - The content to write into the file.
|
|
44
|
-
* @return {Promise<void>} - A promise that resolves when the file has been successfully written.
|
|
45
|
-
* @internal
|
|
46
|
-
*/
|
|
47
|
-
export declare function syncFile(path: string, content: string): Promise<void>;
|
|
48
|
-
/**
|
|
49
|
-
* Checks if a file path should be included and returns the matching pattern
|
|
50
|
-
* @param filePath - The file path to check (relative to the repository root)
|
|
51
|
-
* @param options - Import options containing includes patterns
|
|
52
|
-
* @returns Object with include status and matched pattern, or null if not included
|
|
53
|
-
* @internal
|
|
54
|
-
*/
|
|
55
|
-
export declare function shouldIncludeFile(filePath: string, options: ImportOptions): {
|
|
56
|
-
included: true;
|
|
57
|
-
matchedPattern: MatchedPattern | null;
|
|
58
|
-
} | {
|
|
59
|
-
included: false;
|
|
60
|
-
matchedPattern: null;
|
|
61
|
-
};
|
|
62
|
-
/**
|
|
63
|
-
* Detects asset references in markdown content using regex patterns
|
|
64
|
-
* @param content - The markdown content to parse
|
|
65
|
-
* @param assetPatterns - File extensions to treat as assets
|
|
66
|
-
* @returns Array of detected asset paths
|
|
67
|
-
* @internal
|
|
68
|
-
*/
|
|
69
|
-
export declare function detectAssets(content: string, assetPatterns?: string[]): string[];
|
|
70
|
-
/**
|
|
71
|
-
* Downloads an asset from GitHub and saves it locally
|
|
72
|
-
* @param octokit - GitHub API client
|
|
73
|
-
* @param owner - Repository owner
|
|
74
|
-
* @param repo - Repository name
|
|
75
|
-
* @param ref - Git reference
|
|
76
|
-
* @param assetPath - Path to the asset in the repository
|
|
77
|
-
* @param localPath - Local path where the asset should be saved
|
|
78
|
-
* @param signal - Abort signal for cancellation
|
|
79
|
-
* @returns Promise that resolves when the asset is downloaded
|
|
80
|
-
* @internal
|
|
81
|
-
*/
|
|
82
|
-
export declare function downloadAsset(octokit: any, owner: string, repo: string, ref: string, assetPath: string, localPath: string, signal?: AbortSignal): Promise<void>;
|
|
83
|
-
/**
|
|
84
|
-
* Transforms asset references in markdown content to use local paths
|
|
85
|
-
* @param content - The markdown content to transform
|
|
86
|
-
* @param assetMap - Map of original asset paths to new local paths
|
|
87
|
-
* @returns Transformed content with updated asset references
|
|
88
|
-
* @internal
|
|
89
|
-
*/
|
|
90
|
-
export declare function transformAssetReferences(content: string, assetMap: Map<string, string>): string;
|
|
91
|
-
/**
|
|
92
|
-
* Synchronizes an entry by fetching its contents, validating its metadata, and storing or rendering it as needed.
|
|
93
|
-
*
|
|
94
|
-
* @param {LoaderContext} context - The loader context containing the required utilities, metadata, and configuration.
|
|
95
|
-
* @param {Object} urls - Object containing URL data.
|
|
96
|
-
* @param {string | URL | null} urls.url - The URL of the entry to fetch. Throws an error if null or invalid.
|
|
97
|
-
* @param {string} urls.editUrl - The URL for editing the entry.
|
|
98
|
-
* @param {RootOptions} options - Configuration settings for processing the entry such as file paths and custom options.
|
|
99
|
-
* @param {any} octokit - GitHub API client for downloading assets.
|
|
100
|
-
* @param {RequestInit} [init] - Optional parameter for customizing the fetch request.
|
|
101
|
-
* @return {Promise<void>} Resolves when the entry has been successfully processed and stored. Throws errors if invalid URL, missing configuration, or other issues occur.
|
|
102
|
-
* @internal
|
|
103
|
-
*/
|
|
104
|
-
export declare function syncEntry(context: LoaderContext, { url, editUrl }: {
|
|
105
|
-
url: string | URL | null;
|
|
106
|
-
editUrl: string;
|
|
107
|
-
}, filePath: string, options: ImportOptions, octokit: any, init?: RequestInit): Promise<void>;
|
|
1
|
+
import type { CollectionEntryOptions } from "./github.types.js";
|
|
2
|
+
import { type ImportStats } from "./github.paths.js";
|
|
3
|
+
export { type ImportStats, generateId, generatePath, shouldIncludeFile, applyRename, getHeaders, syncHeaders, } from "./github.paths.js";
|
|
4
|
+
export { syncFile } from "./github.storage.js";
|
|
5
|
+
export { resolveAssetConfig, detectAssets, downloadAsset, transformAssetReferences, } from "./github.assets.js";
|
|
108
6
|
/**
|
|
109
7
|
* Converts a given GitHub repository path into a collection entry by fetching the content
|
|
110
8
|
* from the GitHub repository using the provided Octokit instance and options.
|
|
@@ -112,27 +10,3 @@ export declare function syncEntry(context: LoaderContext, { url, editUrl }: {
|
|
|
112
10
|
* @internal
|
|
113
11
|
*/
|
|
114
12
|
export declare function toCollectionEntry({ context, octokit, options, signal, force, clear, }: CollectionEntryOptions): Promise<ImportStats>;
|
|
115
|
-
/**
|
|
116
|
-
* Get the headers needed to make a conditional request.
|
|
117
|
-
* Uses the etag and last-modified values from the meta store.
|
|
118
|
-
* @internal
|
|
119
|
-
*/
|
|
120
|
-
export declare function getHeaders({ init, meta, id, }: {
|
|
121
|
-
/** Initial headers to include */
|
|
122
|
-
init?: RequestInit["headers"];
|
|
123
|
-
/** Meta store to get etag and last-modified values from */
|
|
124
|
-
meta: LoaderContext["meta"];
|
|
125
|
-
id: string;
|
|
126
|
-
}): Headers;
|
|
127
|
-
/**
|
|
128
|
-
* Store the etag or last-modified headers from a response in the meta store.
|
|
129
|
-
* @internal
|
|
130
|
-
*/
|
|
131
|
-
export declare function syncHeaders({ headers, meta, id, }: {
|
|
132
|
-
/** Headers from the response */
|
|
133
|
-
headers: Headers;
|
|
134
|
-
/** Meta store to store etag and last-modified values in */
|
|
135
|
-
meta: LoaderContext["meta"];
|
|
136
|
-
/** id string */
|
|
137
|
-
id: string;
|
|
138
|
-
}): void;
|