astro-loader-pocketbase 0.3.0-rc.1 → 0.3.0-rc.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-loader-pocketbase",
3
- "version": "0.3.0-rc.1",
3
+ "version": "0.3.0-rc.3",
4
4
  "description": "A content loader for Astro that uses the PocketBase API",
5
5
  "license": "MIT",
6
6
  "author": "Luis Wolf <development@pawcode.de> (https://pawcode.de)",
@@ -23,8 +23,8 @@
23
23
  "devDependencies": {
24
24
  "@eslint/js": "^9.11.1",
25
25
  "@stylistic/eslint-plugin": "^2.8.0",
26
- "@types/node": "^22.5.5",
27
- "astro": "^5.0.0-beta.1",
26
+ "@types/node": "^22.7.4",
27
+ "astro": "^5.0.0-beta.2",
28
28
  "eslint": "^9.11.1",
29
29
  "globals": "^15.9.0",
30
30
  "husky": "^9.1.6",
@@ -5,6 +5,7 @@ import type { PocketBaseCollection } from "./types/pocketbase-schema.type";
5
5
  import { getRemoteSchema } from "./utils/get-remote-schema";
6
6
  import { parseSchema } from "./utils/parse-schema";
7
7
  import { readLocalSchema } from "./utils/read-local-schema";
8
+ import { transformFiles } from "./utils/transform-files";
8
9
 
9
10
  /**
10
11
  * Basic schema for every PocketBase collection.
@@ -84,8 +85,21 @@ export async function generateSchema(
84
85
  const base = collection.type === "view" ? VIEW_SCHEMA : BASIC_SCHEMA;
85
86
 
86
87
  // Combine the basic schema with the parsed fields
87
- return z.object({
88
+ const schema = z.object({
88
89
  ...base,
89
90
  ...fields
90
91
  });
92
+
93
+ // Get all file fields
94
+ const fileFields = collection.schema.filter((field) => field.type === "file");
95
+
96
+ if (fileFields.length === 0) {
97
+ return schema;
98
+ }
99
+
100
+ // Transform file names to file urls
101
+ return schema.transform((entry) =>
102
+ // @ts-expect-error - `updated` and `created` are already transformed to dates
103
+ transformFiles(options.url, fileFields, entry)
104
+ );
91
105
  }
@@ -43,7 +43,7 @@ export async function loadEntries(
43
43
  let page = 1;
44
44
  let totalPages = 0;
45
45
  let entries = 0;
46
- let hasUpdatedColumn = false;
46
+ let hasUpdatedColumn = !!lastModified;
47
47
 
48
48
  // Fetch all (modified) entries
49
49
  do {
@@ -1,4 +1,5 @@
1
1
  import type { Loader, LoaderContext } from "astro/loaders";
2
+ import packageJson from "./../package.json";
2
3
  import { cleanupEntries } from "./cleanup-entries";
3
4
  import { generateSchema } from "./generate-schema";
4
5
  import { loadEntries } from "./load-entries";
@@ -14,6 +15,16 @@ export function pocketbaseLoader(options: PocketBaseLoaderOptions): Loader {
14
15
  return {
15
16
  name: "pocketbase-loader",
16
17
  load: async (context: LoaderContext): Promise<void> => {
18
+ // Check if the version has changed to force an update
19
+ const lastVersion = context.meta.get("version");
20
+ if (lastVersion !== packageJson.version) {
21
+ context.logger.info(
22
+ `PocketBase loader was updated from ${lastVersion} to ${packageJson.version}. All entries will be loaded again.`
23
+ );
24
+
25
+ options.forceUpdate = true;
26
+ }
27
+
17
28
  // Get the date of the last fetch to only update changed entries.
18
29
  // If `forceUpdate` is set to `true`, this will be `undefined` to fetch all entries again.
19
30
  const lastModified = options.forceUpdate
@@ -67,6 +78,8 @@ export function pocketbaseLoader(options: PocketBaseLoaderOptions): Loader {
67
78
 
68
79
  // Set the last modified date to the current date
69
80
  context.meta.set("last-modified", new Date().toISOString());
81
+
82
+ context.meta.set("version", packageJson.version);
70
83
  },
71
84
  schema: async () => {
72
85
  // Generate the schema for the collection according to the API
@@ -19,7 +19,7 @@ export interface PocketBaseLoaderOptions {
19
19
  * The loader will concatenate the content of all fields in the order they are defined in the array.
20
20
  * Each block will be contained in a `<section>` element.
21
21
  */
22
- content: string | Array<string>;
22
+ content?: string | Array<string>;
23
23
  /**
24
24
  * Email of an admin to get full access to the PocketBase instance.
25
25
  * Together with `adminPassword` this is required to get automatic type generation and to access all resources even if they are not public.
@@ -12,7 +12,7 @@ import type { PocketBaseEntry } from "../types/pocketbase-entry.type";
12
12
  export async function parseEntry(
13
13
  entry: PocketBaseEntry,
14
14
  { generateDigest, parseData, store }: LoaderContext,
15
- contentFields: string | Array<string>
15
+ contentFields?: string | Array<string>
16
16
  ): Promise<void> {
17
17
  // Parse the data to match the schema
18
18
  // This will throw an error if the data does not match the schema
@@ -27,6 +27,16 @@ export async function parseEntry(
27
27
  // View collections don't necessarily publish the updated date, so the whole entry is used for the digest.
28
28
  const digest = generateDigest(entry.updated ?? entry.created ?? entry);
29
29
 
30
+ if (!contentFields) {
31
+ // Store the entry
32
+ store.set({
33
+ id: entry.id,
34
+ data,
35
+ digest
36
+ });
37
+ return;
38
+ }
39
+
30
40
  // Generate the content for the entry
31
41
  let content: string;
32
42
  if (typeof contentFields === "string") {
@@ -44,7 +44,8 @@ export function parseSchema(
44
44
  break;
45
45
  case "relation":
46
46
  case "file":
47
- // NOTE: Relations and files are currently not supported and are treated as strings
47
+ // NOTE: Relations are currently not supported and are treated as strings
48
+ // NOTE: Files are later transformed to URLs
48
49
 
49
50
  // Parse the field type based on the number of values it can have
50
51
  fieldType = parseSingleOrMultipleValues(field, z.string());
@@ -0,0 +1,66 @@
1
+ import type { PocketBaseEntry } from "../types/pocketbase-entry.type";
2
+ import type { PocketBaseSchemaEntry } from "../types/pocketbase-schema.type";
3
+
4
+ /**
5
+ * Transforms file names in a PocketBase entry to file URLs.
6
+ *
7
+ * @param baseUrl URL of the PocketBase instance.
8
+ * @param collection Collection of the entry.
9
+ * @param entry Entry to transform.
10
+ */
11
+ export function transformFiles(
12
+ baseUrl: string,
13
+ fileFields: Array<PocketBaseSchemaEntry>,
14
+ entry: PocketBaseEntry
15
+ ): PocketBaseEntry {
16
+ // Transform all file names to file URLs
17
+ for (const field of fileFields) {
18
+ const fieldName = field.name;
19
+
20
+ if (field.options.maxSelect === 1) {
21
+ const fileName = entry[fieldName] as string | undefined;
22
+ // Check if a file name is present
23
+ if (!fileName) {
24
+ continue;
25
+ }
26
+
27
+ // Transform the file name to a file URL
28
+ entry[fieldName] = transformFileUrl(
29
+ baseUrl,
30
+ entry.collectionName,
31
+ entry.id,
32
+ fileName
33
+ );
34
+ } else {
35
+ const fileNames = entry[fieldName] as Array<string> | undefined;
36
+ // Check if file names are present
37
+ if (!fileNames) {
38
+ continue;
39
+ }
40
+
41
+ // Transform all file names to file URLs
42
+ entry[fieldName] = fileNames.map((file) =>
43
+ transformFileUrl(baseUrl, entry.collectionName, entry.id, file)
44
+ );
45
+ }
46
+ }
47
+
48
+ return entry;
49
+ }
50
+
51
+ /**
52
+ * Transforms a file name to a PocketBase file URL.
53
+ *
54
+ * @param base Base URL of the PocketBase instance.
55
+ * @param collectionName Name of the collection.
56
+ * @param entryId ID of the entry.
57
+ * @param file Name of the file.
58
+ */
59
+ function transformFileUrl(
60
+ base: string,
61
+ collectionName: string,
62
+ entryId: string,
63
+ file: string
64
+ ): string {
65
+ return `${base}/api/files/${collectionName}/${entryId}/${file}`;
66
+ }