@studiocms/cfetch 0.2.1 → 0.3.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 +59 -8
- package/dist/{cache.d.ts → cache.d.mts} +28 -31
- package/dist/cache.mjs +176 -0
- package/dist/consts.d.mts +17 -0
- package/dist/consts.mjs +23 -0
- package/dist/index.d.mts +32 -0
- package/dist/index.mjs +62 -0
- package/dist/stub.d.mts +22 -0
- package/dist/{stub.js → stub.mjs} +77 -19
- package/dist/{types.d.ts → types.d.mts} +9 -5
- package/dist/types.mjs +1 -0
- package/dist/utils/{integration.d.ts → integration.d.mts} +31 -32
- package/dist/utils/integration.mjs +276 -0
- package/dist/{wrappers.d.ts → wrappers.d.mts} +77 -40
- package/dist/wrappers.mjs +364 -0
- package/package.json +13 -10
- package/dist/cache.js +0 -89
- package/dist/consts.d.ts +0 -8
- package/dist/consts.js +0 -11
- package/dist/index.d.ts +0 -66
- package/dist/index.js +0 -42
- package/dist/stub.d.ts +0 -6
- package/dist/types.js +0 -0
- package/dist/utils/integration.js +0 -266
- package/dist/wrappers.js +0 -87
|
@@ -1,19 +1,39 @@
|
|
|
1
|
-
import { createResolver } from "./utils/integration.
|
|
1
|
+
import { createResolver } from "./utils/integration.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/stub.ts
|
|
4
|
+
/**
|
|
5
|
+
* This file serves as a stub for TypeScript type definitions related to the cFetch integration.
|
|
6
|
+
* It defines the structure of virtual modules and the types that will be injected into the consuming project.
|
|
7
|
+
* The actual implementation of these types is provided in the corresponding .mjs files, but this stub allows
|
|
8
|
+
* for proper type checking and IntelliSense in development environments.
|
|
9
|
+
*
|
|
10
|
+
* The stub includes module declarations for 'virtual:cfetch/config' and 'c:fetch', defining the expected types
|
|
11
|
+
* and exports that will be available when using the cFetch integration in an Astro project.
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* - The 'virtual:cfetch/config' module provides access to the cache configuration, which can be customized by users.
|
|
15
|
+
* - The 'c:fetch' module exports various types and functions related to the cached fetch functionality, including error types, parsers, and the main cFetch function.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
2
19
|
const { resolve } = createResolver(import.meta.url);
|
|
20
|
+
/**
|
|
21
|
+
* Stub content for TypeScript type definitions related to the cFetch integration.
|
|
22
|
+
*/
|
|
3
23
|
const stub = `
|
|
4
24
|
declare module 'virtual:cfetch/config' {
|
|
5
|
-
type CacheConfig = import("${resolve("./types.
|
|
25
|
+
type CacheConfig = import("${resolve("./types.mjs")}").CacheConfigLive;
|
|
6
26
|
const defaultConfig: CacheConfig;
|
|
7
27
|
export default defaultConfig;
|
|
8
28
|
}
|
|
9
29
|
|
|
10
30
|
declare module 'c:fetch' {
|
|
11
|
-
export type CacheConfig = import("${resolve("./types.
|
|
12
|
-
export type CachedResponse<T> = import("${resolve("./wrappers.
|
|
13
|
-
export type CFetchConfig = import("${resolve("./wrappers.
|
|
31
|
+
export type CacheConfig = import("${resolve("./types.mjs")}").CacheConfig;
|
|
32
|
+
export type CachedResponse<T> = import("${resolve("./wrappers.mjs")}").CachedResponse<T>;
|
|
33
|
+
export type CFetchConfig = import("${resolve("./wrappers.mjs")}").CFetchConfig;
|
|
34
|
+
export type InvalidateCacheOptions = import("${resolve("./wrappers.mjs")}").InvalidateCacheOptions;
|
|
14
35
|
|
|
15
|
-
export const Duration: typeof import("${resolve("./wrappers.
|
|
16
|
-
|
|
36
|
+
export const Duration: typeof import("${resolve("./wrappers.mjs")}").Duration;
|
|
17
37
|
declare const FetchError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
18
38
|
readonly _tag: "FetchError";
|
|
19
39
|
} & Readonly<A>;
|
|
@@ -34,7 +54,7 @@ declare module 'c:fetch' {
|
|
|
34
54
|
* @param _ - The Response object (ignored)
|
|
35
55
|
* @returns A Promise that resolves to undefined
|
|
36
56
|
*/
|
|
37
|
-
export const noOpParser: typeof import("${resolve("./wrappers.
|
|
57
|
+
export const noOpParser: typeof import("${resolve("./wrappers.mjs")}").noOpParser;
|
|
38
58
|
|
|
39
59
|
/**
|
|
40
60
|
* Fetches data from a URL with caching capabilities using Effect.
|
|
@@ -63,7 +83,26 @@ declare module 'c:fetch' {
|
|
|
63
83
|
* - Cache hits are logged to console for debugging
|
|
64
84
|
* - The effect is provided with CacheLive layer automatically
|
|
65
85
|
*/
|
|
66
|
-
export const cFetchEffect: typeof import("${resolve("./wrappers.
|
|
86
|
+
export const cFetchEffect: typeof import("${resolve("./wrappers.mjs")}").cFetchEffect;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Invalidates cache entries based on specified keys or tags.
|
|
90
|
+
*
|
|
91
|
+
* @param opts - An object containing optional keys and tags for cache invalidation
|
|
92
|
+
* @param opts.keys - An array of specific cache keys to invalidate
|
|
93
|
+
* @param opts.tags - An array of tags; all cache entries associated with these tags will be invalidated
|
|
94
|
+
*
|
|
95
|
+
* @returns An Effect that performs the cache invalidation when executed
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* \`\`\`typescript
|
|
99
|
+
* yield* invalidateCacheEffect({
|
|
100
|
+
* tags: ['user'],
|
|
101
|
+
* keys: ['user:123', 'user:456']
|
|
102
|
+
* });
|
|
103
|
+
* \`\`\`
|
|
104
|
+
*/
|
|
105
|
+
export const invalidateCacheEffect: typeof import("${resolve("./wrappers.mjs")}").invalidateCacheEffect;
|
|
67
106
|
|
|
68
107
|
/**
|
|
69
108
|
* Creates an Effect that fetches JSON data from a URL with caching support.
|
|
@@ -77,7 +116,7 @@ declare module 'c:fetch' {
|
|
|
77
116
|
* @param cacheConfig.key - Custom cache key to use instead of the default URL-based key
|
|
78
117
|
* @returns An Effect that yields a CachedResponse containing the parsed JSON data, or fails with a FetchError
|
|
79
118
|
*/
|
|
80
|
-
export const cFetchEffectJson: typeof import("${resolve("./wrappers.
|
|
119
|
+
export const cFetchEffectJson: typeof import("${resolve("./wrappers.mjs")}").cFetchEffectJson;
|
|
81
120
|
|
|
82
121
|
|
|
83
122
|
/**
|
|
@@ -93,7 +132,7 @@ declare module 'c:fetch' {
|
|
|
93
132
|
* @returns An Effect that resolves to a CachedResponse containing the response text,
|
|
94
133
|
* or fails with a FetchError if the request fails.
|
|
95
134
|
*/
|
|
96
|
-
export const cFetchEffectText: typeof import("${resolve("./wrappers.
|
|
135
|
+
export const cFetchEffectText: typeof import("${resolve("./wrappers.mjs")}").cFetchEffectText;
|
|
97
136
|
|
|
98
137
|
/**
|
|
99
138
|
* Fetches a resource and returns it as a Blob with caching support.
|
|
@@ -107,7 +146,7 @@ declare module 'c:fetch' {
|
|
|
107
146
|
*
|
|
108
147
|
* @returns An Effect that resolves to a CachedResponse containing a Blob, or fails with a FetchError
|
|
109
148
|
*/
|
|
110
|
-
export const cFetchEffectBlob: typeof import("${resolve("./wrappers.
|
|
149
|
+
export const cFetchEffectBlob: typeof import("${resolve("./wrappers.mjs")}").cFetchEffectBlob;
|
|
111
150
|
|
|
112
151
|
/**
|
|
113
152
|
* Executes a cached fetch request with configurable caching behavior.
|
|
@@ -122,7 +161,7 @@ declare module 'c:fetch' {
|
|
|
122
161
|
* @param cacheConfig.key - Custom cache key; if not provided, a key will be generated from the URL and options
|
|
123
162
|
* @returns A Promise that resolves to a CachedResponse containing the parsed data
|
|
124
163
|
*/
|
|
125
|
-
export const cFetch: typeof import("${resolve("./wrappers.
|
|
164
|
+
export const cFetch: typeof import("${resolve("./wrappers.mjs")}").cFetch;
|
|
126
165
|
|
|
127
166
|
/**
|
|
128
167
|
* Fetches and parses JSON data from the specified URL with caching support.
|
|
@@ -136,7 +175,7 @@ declare module 'c:fetch' {
|
|
|
136
175
|
* @param cacheConfig.key - Custom cache key to use instead of the default
|
|
137
176
|
* @returns A Promise that resolves to a CachedResponse containing the parsed JSON data of type T
|
|
138
177
|
*/
|
|
139
|
-
export const cFetchJson: typeof import("${resolve("./wrappers.
|
|
178
|
+
export const cFetchJson: typeof import("${resolve("./wrappers.mjs")}").cFetchJson;
|
|
140
179
|
|
|
141
180
|
/**
|
|
142
181
|
* Fetches a URL and returns the response as text with caching support.
|
|
@@ -149,7 +188,7 @@ declare module 'c:fetch' {
|
|
|
149
188
|
* @param cacheConfig.key - Custom cache key (defaults to URL if not provided)
|
|
150
189
|
* @returns A promise that resolves to a CachedResponse containing the response text
|
|
151
190
|
*/
|
|
152
|
-
export const cFetchText: typeof import("${resolve("./wrappers.
|
|
191
|
+
export const cFetchText: typeof import("${resolve("./wrappers.mjs")}").cFetchText;
|
|
153
192
|
|
|
154
193
|
/**
|
|
155
194
|
* Fetches a Blob resource from the specified URL with optional caching configuration.
|
|
@@ -162,11 +201,30 @@ declare module 'c:fetch' {
|
|
|
162
201
|
* @param cacheConfig.key - Custom cache key for storing the response
|
|
163
202
|
* @returns A Promise that resolves to a CachedResponse containing a Blob
|
|
164
203
|
*/
|
|
165
|
-
export const cFetchBlob: typeof import("${resolve("./wrappers.
|
|
204
|
+
export const cFetchBlob: typeof import("${resolve("./wrappers.mjs")}").cFetchBlob;
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Invalidates cache entries based on specified keys or tags.
|
|
208
|
+
*
|
|
209
|
+
* @param opts - An object containing optional keys and tags for cache invalidation
|
|
210
|
+
* @param opts.keys - An array of specific cache keys to invalidate
|
|
211
|
+
* @param opts.tags - An array of tags; all cache entries associated with these tags will be invalidated
|
|
212
|
+
*
|
|
213
|
+
* @returns A Promise that resolves when the cache invalidation is complete
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* \`\`\`typescript
|
|
217
|
+
* await invalidateCache({
|
|
218
|
+
* tags: ['user'],
|
|
219
|
+
* keys: ['user:123', 'user:456']
|
|
220
|
+
* });
|
|
221
|
+
* \`\`\`
|
|
222
|
+
*/
|
|
223
|
+
export const invalidateCache: typeof import("${resolve("./wrappers.mjs")}").invalidateCache;
|
|
166
224
|
}
|
|
167
225
|
|
|
168
226
|
`;
|
|
169
227
|
var stub_default = stub;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
};
|
|
228
|
+
|
|
229
|
+
//#endregion
|
|
230
|
+
export { stub_default as default };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Duration } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
2
4
|
/**
|
|
3
5
|
* Configuration options for caching behavior.
|
|
4
6
|
*
|
|
@@ -12,8 +14,8 @@ import type { Duration } from 'effect';
|
|
|
12
14
|
* };
|
|
13
15
|
* ```
|
|
14
16
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
type CacheConfig = {
|
|
18
|
+
lifetime: Duration.DurationInput;
|
|
17
19
|
};
|
|
18
20
|
/**
|
|
19
21
|
* Configuration options for cache lifetime management.
|
|
@@ -29,6 +31,8 @@ export type CacheConfig = {
|
|
|
29
31
|
* };
|
|
30
32
|
* ```
|
|
31
33
|
*/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
type CacheConfigLive = {
|
|
35
|
+
lifetime: number;
|
|
34
36
|
};
|
|
37
|
+
//#endregion
|
|
38
|
+
export { CacheConfig, CacheConfigLive };
|
package/dist/types.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -1,23 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export declare const isAbsolute: typeof path.isAbsolute;
|
|
9
|
-
export declare const dirname: typeof path.dirname;
|
|
1
|
+
import { HookParameters } from "astro";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { PluginOption } from "vite";
|
|
4
|
+
|
|
5
|
+
//#region src/utils/integration.d.ts
|
|
6
|
+
declare const isAbsolute: typeof path.isAbsolute;
|
|
7
|
+
declare const dirname: typeof path.dirname;
|
|
10
8
|
/**
|
|
11
9
|
* Resolves a string path, resolving '.' and '.' segments and allowing paths above the root.
|
|
12
10
|
*
|
|
13
|
-
* @param path - The path to
|
|
11
|
+
* @param path - The path to normalize.
|
|
14
12
|
* @param allowAboveRoot - Whether to allow the resulting path to be above the root directory.
|
|
15
|
-
* @returns the
|
|
13
|
+
* @returns the normalized path string.
|
|
16
14
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
declare function normalizeString(path: string, allowAboveRoot: boolean): string;
|
|
16
|
+
declare function normalizeWindowsPath(input?: string): string;
|
|
17
|
+
declare const resolve: typeof path.resolve;
|
|
18
|
+
type Hooks = Required<Astro.IntegrationHooks>;
|
|
21
19
|
/**
|
|
22
20
|
* Allows resolving paths relatively to the integration folder easily. Call it like this:
|
|
23
21
|
*
|
|
@@ -33,15 +31,15 @@ export type Hooks = Required<Astro.IntegrationHooks>;
|
|
|
33
31
|
*
|
|
34
32
|
* This way, you do not have to add your plugin to your package.json `exports`.
|
|
35
33
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
declare const createResolver: (_base: string) => {
|
|
35
|
+
resolve: (...path: Array<string>) => string;
|
|
38
36
|
};
|
|
39
37
|
/**
|
|
40
38
|
* A utility to be used on an Astro hook.
|
|
41
39
|
*
|
|
42
40
|
* @see defineUtility
|
|
43
41
|
*/
|
|
44
|
-
|
|
42
|
+
type HookUtility<THook extends keyof Hooks, TArgs extends Array<any>, TReturn> = (params: HookParameters<THook>, ...args: TArgs) => TReturn;
|
|
45
43
|
/**
|
|
46
44
|
* Allows defining a type-safe function requiring all the params of a given hook.
|
|
47
45
|
* It uses currying to make TypeScript happy.
|
|
@@ -57,7 +55,7 @@ export type HookUtility<THook extends keyof Hooks, TArgs extends Array<any>, TRe
|
|
|
57
55
|
* });
|
|
58
56
|
* ```
|
|
59
57
|
*/
|
|
60
|
-
|
|
58
|
+
declare const defineUtility: <THook extends keyof Hooks>(_hook: THook) => <TArgs extends Array<any>, T>(fn: HookUtility<THook, TArgs, T>) => HookUtility<THook, TArgs, T>;
|
|
61
59
|
/**
|
|
62
60
|
* Checks for the existence of a Vite plugin inside the Astro config.
|
|
63
61
|
*
|
|
@@ -74,8 +72,8 @@ export declare const defineUtility: <THook extends keyof Hooks>(_hook: THook) =>
|
|
|
74
72
|
* })
|
|
75
73
|
* ```
|
|
76
74
|
*/
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
declare const hasVitePlugin: HookUtility<"astro:config:setup", [{
|
|
76
|
+
plugin: string | PluginOption;
|
|
79
77
|
}], boolean>;
|
|
80
78
|
/**
|
|
81
79
|
* Adds a [vite plugin](https://vitejs.dev/guide/using-plugins) to the
|
|
@@ -96,14 +94,14 @@ export declare const hasVitePlugin: HookUtility<"astro:config:setup", [{
|
|
|
96
94
|
* })
|
|
97
95
|
* ```
|
|
98
96
|
*/
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
declare const addVitePlugin: HookUtility<"astro:config:setup", [{
|
|
98
|
+
plugin: PluginOption;
|
|
99
|
+
warnDuplicated?: boolean;
|
|
102
100
|
}], void>;
|
|
103
101
|
type VirtualImport = {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
id: string;
|
|
103
|
+
content: string;
|
|
104
|
+
context?: 'server' | 'client' | undefined;
|
|
107
105
|
};
|
|
108
106
|
type Imports = Record<string, string> | Array<VirtualImport>;
|
|
109
107
|
/**
|
|
@@ -139,9 +137,10 @@ type Imports = Record<string, string> | Array<VirtualImport>;
|
|
|
139
137
|
* console.log(config.foo) // "bar"
|
|
140
138
|
* ```
|
|
141
139
|
*/
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
140
|
+
declare const addVirtualImports: HookUtility<"astro:config:setup", [{
|
|
141
|
+
name: string;
|
|
142
|
+
imports: Imports;
|
|
143
|
+
__enableCorePowerDoNotUseOrYouWillBeFired?: boolean;
|
|
146
144
|
}], void>;
|
|
147
|
-
|
|
145
|
+
//#endregion
|
|
146
|
+
export { HookUtility, Hooks, addVirtualImports, addVitePlugin, createResolver, defineUtility, dirname, hasVitePlugin, isAbsolute, normalizeString, normalizeWindowsPath, resolve };
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/integration.ts
|
|
4
|
+
const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
|
|
5
|
+
const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
|
|
6
|
+
const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
|
|
7
|
+
const isAbsolute = (p) => _IS_ABSOLUTE_RE.test(p);
|
|
8
|
+
const dirname = (p) => {
|
|
9
|
+
const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
|
|
10
|
+
if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) segments[0] += "/";
|
|
11
|
+
return segments.join("/") || (isAbsolute(p) ? "/" : ".");
|
|
12
|
+
};
|
|
13
|
+
function cwd() {
|
|
14
|
+
if (typeof process !== "undefined" && typeof process.cwd === "function") return process.cwd().replace(/\\/g, "/");
|
|
15
|
+
return "/";
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Resolves a string path, resolving '.' and '.' segments and allowing paths above the root.
|
|
19
|
+
*
|
|
20
|
+
* @param path - The path to normalize.
|
|
21
|
+
* @param allowAboveRoot - Whether to allow the resulting path to be above the root directory.
|
|
22
|
+
* @returns the normalized path string.
|
|
23
|
+
*/
|
|
24
|
+
function normalizeString(path, allowAboveRoot) {
|
|
25
|
+
let res = "";
|
|
26
|
+
let lastSegmentLength = 0;
|
|
27
|
+
let lastSlash = -1;
|
|
28
|
+
let dots = 0;
|
|
29
|
+
let char = null;
|
|
30
|
+
for (let index = 0; index <= path.length; ++index) {
|
|
31
|
+
if (index < path.length) char = path[index];
|
|
32
|
+
else if (char === "/") break;
|
|
33
|
+
else char = "/";
|
|
34
|
+
if (char === "/") {
|
|
35
|
+
if (lastSlash === index - 1 || dots === 1) {} else if (dots === 2) {
|
|
36
|
+
if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
|
|
37
|
+
if (res.length > 2) {
|
|
38
|
+
const lastSlashIndex = res.lastIndexOf("/");
|
|
39
|
+
if (lastSlashIndex === -1) {
|
|
40
|
+
res = "";
|
|
41
|
+
lastSegmentLength = 0;
|
|
42
|
+
} else {
|
|
43
|
+
res = res.slice(0, lastSlashIndex);
|
|
44
|
+
lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
|
|
45
|
+
}
|
|
46
|
+
lastSlash = index;
|
|
47
|
+
dots = 0;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (res.length > 0) {
|
|
51
|
+
res = "";
|
|
52
|
+
lastSegmentLength = 0;
|
|
53
|
+
lastSlash = index;
|
|
54
|
+
dots = 0;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (allowAboveRoot) {
|
|
59
|
+
res += res.length > 0 ? "/.." : "..";
|
|
60
|
+
lastSegmentLength = 2;
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
if (res.length > 0) res += `/${path.slice(lastSlash + 1, index)}`;
|
|
64
|
+
else res = path.slice(lastSlash + 1, index);
|
|
65
|
+
lastSegmentLength = index - lastSlash - 1;
|
|
66
|
+
}
|
|
67
|
+
lastSlash = index;
|
|
68
|
+
dots = 0;
|
|
69
|
+
} else if (char === "." && dots !== -1) ++dots;
|
|
70
|
+
else dots = -1;
|
|
71
|
+
}
|
|
72
|
+
return res;
|
|
73
|
+
}
|
|
74
|
+
function normalizeWindowsPath(input = "") {
|
|
75
|
+
if (!input) return input;
|
|
76
|
+
return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
|
|
77
|
+
}
|
|
78
|
+
const resolve = (...arguments_) => {
|
|
79
|
+
arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
|
|
80
|
+
let resolvedPath = "";
|
|
81
|
+
let resolvedAbsolute = false;
|
|
82
|
+
for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
|
|
83
|
+
const path = index >= 0 ? arguments_[index] : cwd();
|
|
84
|
+
if (!path || path.length === 0) continue;
|
|
85
|
+
resolvedPath = `${path}/${resolvedPath}`;
|
|
86
|
+
resolvedAbsolute = isAbsolute(path);
|
|
87
|
+
}
|
|
88
|
+
resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
|
|
89
|
+
if (resolvedAbsolute && !isAbsolute(resolvedPath)) return `/${resolvedPath}`;
|
|
90
|
+
return resolvedPath.length > 0 ? resolvedPath : ".";
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Allows resolving paths relatively to the integration folder easily. Call it like this:
|
|
94
|
+
*
|
|
95
|
+
* @param {string} _base - The location you want to create relative references from. `import.meta.url` is usually what you'll want.
|
|
96
|
+
*
|
|
97
|
+
* @see https://astro-integration-kit.netlify.app/core/create-resolver/
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* const { resolve } = createResolver(import.meta.url);
|
|
102
|
+
* const pluginPath = resolve("./plugin.ts");
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* This way, you do not have to add your plugin to your package.json `exports`.
|
|
106
|
+
*/
|
|
107
|
+
const createResolver = (_base) => {
|
|
108
|
+
let base = _base;
|
|
109
|
+
if (base.startsWith("file://")) base = dirname(fileURLToPath(base));
|
|
110
|
+
return { resolve: (...path) => resolve(base, ...path) };
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Allows defining a type-safe function requiring all the params of a given hook.
|
|
114
|
+
* It uses currying to make TypeScript happy.
|
|
115
|
+
*
|
|
116
|
+
* @param {string} _hook
|
|
117
|
+
*
|
|
118
|
+
* @see https://astro-integration-kit.netlify.app/core/define-utility/
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* const test = defineUtility("astro:config:setup")((params, foo: boolean) => {
|
|
123
|
+
* return "bar";
|
|
124
|
+
* });
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
const defineUtility = (_hook) => (fn) => fn;
|
|
128
|
+
function getPluginNames(plugins) {
|
|
129
|
+
const names = [];
|
|
130
|
+
if (plugins) for (const plugin of plugins) {
|
|
131
|
+
if (!plugin) continue;
|
|
132
|
+
if (Array.isArray(plugin)) {
|
|
133
|
+
names.push(...getPluginNames(plugin));
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (plugin instanceof Promise) continue;
|
|
137
|
+
names.push(plugin.name);
|
|
138
|
+
}
|
|
139
|
+
return names;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Checks for the existence of a Vite plugin inside the Astro config.
|
|
143
|
+
*
|
|
144
|
+
* @param {import("astro").HookParameters<"astro:config:setup">} params
|
|
145
|
+
* @param {Params} options
|
|
146
|
+
* @param {string | import("vite").PluginOption} options.plugin
|
|
147
|
+
*
|
|
148
|
+
* @see https://astro-integration-kit.netlify.app/utilities/has-vite-plugin/
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```ts
|
|
152
|
+
* hasVitePlugin(params, {
|
|
153
|
+
* plugin: "vite-plugin-my-integration",
|
|
154
|
+
* })
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
const hasVitePlugin = defineUtility("astro:config:setup")(({ config }, { plugin }) => {
|
|
158
|
+
if (!plugin || plugin instanceof Promise) return false;
|
|
159
|
+
const currentPlugins = new Set(getPluginNames(config?.vite?.plugins));
|
|
160
|
+
const plugins = /* @__PURE__ */ new Set();
|
|
161
|
+
if (typeof plugin === "string") plugins.add(plugin);
|
|
162
|
+
if (typeof plugin === "object") if (Array.isArray(plugin)) {
|
|
163
|
+
const names = new Set(getPluginNames(plugin));
|
|
164
|
+
for (const name of names) plugins.add(name);
|
|
165
|
+
} else plugins.add(plugin.name);
|
|
166
|
+
return [...plugins].some((name) => currentPlugins.has(name));
|
|
167
|
+
});
|
|
168
|
+
/**
|
|
169
|
+
* Adds a [vite plugin](https://vitejs.dev/guide/using-plugins) to the
|
|
170
|
+
* Astro config.
|
|
171
|
+
*
|
|
172
|
+
* @param {import("astro").HookParameters<"astro:config:setup">} params
|
|
173
|
+
* @param {object} options
|
|
174
|
+
* @param {import("vite").PluginOption} options.plugin
|
|
175
|
+
* @param {boolean} [options.warnDuplicated=true]
|
|
176
|
+
*
|
|
177
|
+
* @see https://astro-integration-kit.netlify.app/utilities/add-vite-plugin/
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```ts
|
|
181
|
+
* addVitePlugin(params, {
|
|
182
|
+
* plugin,
|
|
183
|
+
* warnDuplicated: true,
|
|
184
|
+
* })
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
const addVitePlugin = defineUtility("astro:config:setup")((params, { plugin, warnDuplicated = true }) => {
|
|
188
|
+
const { updateConfig, logger } = params;
|
|
189
|
+
if (warnDuplicated && hasVitePlugin(params, { plugin })) logger.warn(`The Vite plugin "${plugin.name}" is already present in your Vite configuration, this plugin may not behave correctly.`);
|
|
190
|
+
updateConfig({ vite: { plugins: [plugin] } });
|
|
191
|
+
});
|
|
192
|
+
const incrementPluginName = (name) => {
|
|
193
|
+
let count = 1;
|
|
194
|
+
return `${name.replace(/-(\d+)$/, (_, c) => {
|
|
195
|
+
count = Number.parseInt(c) + 1;
|
|
196
|
+
return "";
|
|
197
|
+
})}-${count}`;
|
|
198
|
+
};
|
|
199
|
+
const resolveVirtualModuleId = (id) => {
|
|
200
|
+
return `\0${id}`;
|
|
201
|
+
};
|
|
202
|
+
const createVirtualModule = (name, _imports, bypassCoreValidation) => {
|
|
203
|
+
const imports = Array.isArray(_imports) ? _imports : Object.entries(_imports).map(([id, content]) => ({
|
|
204
|
+
id,
|
|
205
|
+
content,
|
|
206
|
+
context: void 0
|
|
207
|
+
}));
|
|
208
|
+
const duplicatedImports = {};
|
|
209
|
+
for (const { id, context } of imports) {
|
|
210
|
+
duplicatedImports[id] ??= [];
|
|
211
|
+
duplicatedImports[id]?.push(...context === void 0 ? ["server", "client"] : [context]);
|
|
212
|
+
}
|
|
213
|
+
for (const [id, contexts] of Object.entries(duplicatedImports)) if (contexts.length !== [...new Set(contexts)].length) throw new Error(`Virtual import with id "${id}" has been registered several times with conflicting contexts.`);
|
|
214
|
+
const resolutionMap = Object.fromEntries(imports.map(({ id }) => {
|
|
215
|
+
if (!bypassCoreValidation && id.startsWith("astro:")) throw new Error(`Virtual import name prefix can't be "astro:" (for "${id}") because it's reserved for Astro core.`);
|
|
216
|
+
return [resolveVirtualModuleId(id), id];
|
|
217
|
+
}));
|
|
218
|
+
return {
|
|
219
|
+
name,
|
|
220
|
+
resolveId(id) {
|
|
221
|
+
if (imports.find((_import) => _import.id === id)) return resolveVirtualModuleId(id);
|
|
222
|
+
},
|
|
223
|
+
load(id, options) {
|
|
224
|
+
const resolution = resolutionMap[id];
|
|
225
|
+
if (resolution) {
|
|
226
|
+
const context = options?.ssr ? "server" : "client";
|
|
227
|
+
const data = imports.find((_import) => _import.id === resolution && (_import.context === void 0 || _import.context === context));
|
|
228
|
+
if (data) return data.content;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* Creates a Vite virtual module and updates the Astro config.
|
|
235
|
+
* Virtual imports are useful for passing things like config options, or data computed within the integration.
|
|
236
|
+
*
|
|
237
|
+
* @param {import("astro").HookParameters<"astro:config:setup">} params
|
|
238
|
+
* @param {object} options
|
|
239
|
+
* @param {string} options.name
|
|
240
|
+
* @param {Imports} options.imports
|
|
241
|
+
*
|
|
242
|
+
* @see https://astro-integration-kit.netlify.app/utilities/add-virtual-imports/
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* ```ts
|
|
246
|
+
* // my-integration/index.ts
|
|
247
|
+
* import { addVirtualImports } from "astro-integration-kit";
|
|
248
|
+
*
|
|
249
|
+
* addVirtualImports(params, {
|
|
250
|
+
* name: 'my-integration',
|
|
251
|
+
* imports: {
|
|
252
|
+
* 'virtual:my-integration/config': `export default ${ JSON.stringify({foo: "bar"}) }`,
|
|
253
|
+
* },
|
|
254
|
+
* });
|
|
255
|
+
* ```
|
|
256
|
+
*
|
|
257
|
+
* This is then readable anywhere else in your integration:
|
|
258
|
+
*
|
|
259
|
+
* ```ts
|
|
260
|
+
* // myIntegration/src/component/layout.astro
|
|
261
|
+
* import config from "virtual:my-integration/config";
|
|
262
|
+
*
|
|
263
|
+
* console.log(config.foo) // "bar"
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
const addVirtualImports = defineUtility("astro:config:setup")((params, { name, imports, __enableCorePowerDoNotUseOrYouWillBeFired = false }) => {
|
|
267
|
+
let pluginName = `vite-plugin-${name}`;
|
|
268
|
+
while (hasVitePlugin(params, { plugin: pluginName })) pluginName = incrementPluginName(pluginName);
|
|
269
|
+
addVitePlugin(params, {
|
|
270
|
+
warnDuplicated: false,
|
|
271
|
+
plugin: createVirtualModule(pluginName, imports, __enableCorePowerDoNotUseOrYouWillBeFired)
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
//#endregion
|
|
276
|
+
export { addVirtualImports, addVitePlugin, createResolver, defineUtility, dirname, hasVitePlugin, isAbsolute, normalizeString, normalizeWindowsPath, resolve };
|