@lightnet/sveltia-admin 4.2.2 → 4.2.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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @lightnet/sveltia-admin
2
2
 
3
+ ## 4.2.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [#456](https://github.com/LightNetDev/LightNet/pull/456) [`2c49737`](https://github.com/LightNetDev/LightNet/commit/2c4973792aa695a2b19acc04744aca39bb7f868d) - Serve the development admin as plain HTML loading Sveltia from the CDN, pinned to the installed npm package version, to prevent reloads from interrupting local saves. Keep normal Astro bundling in production and preserve custom admin paths and Astro base paths without requiring files in the site's public directory.
8
+
9
+ - [#456](https://github.com/LightNetDev/LightNet/pull/456) [`2c49737`](https://github.com/LightNetDev/LightNet/commit/2c4973792aa695a2b19acc04744aca39bb7f868d) - Update dependencies.
10
+ - Updated dependencies [[`2c49737`](https://github.com/LightNetDev/LightNet/commit/2c4973792aa695a2b19acc04744aca39bb7f868d), [`2c49737`](https://github.com/LightNetDev/LightNet/commit/2c4973792aa695a2b19acc04744aca39bb7f868d)]:
11
+ - lightnet@4.6.2
12
+
3
13
  ## 4.2.2
4
14
 
5
15
  ### Patch Changes
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Admin UI for LightNet based on Sveltia CMS.",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
- "version": "4.2.2",
6
+ "version": "4.2.3",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -26,29 +26,30 @@
26
26
  ],
27
27
  "exports": {
28
28
  ".": "./src/astro-integration/integration.ts",
29
+ "./admin.html.ts": "./src/admin.html.ts",
29
30
  "./Admin.astro": "./src/Admin.astro",
30
31
  "./config.json.ts": "./src/config.json.ts"
31
32
  },
32
33
  "peerDependencies": {
33
34
  "astro": "^6.0.0",
34
- "lightnet": "^4.6.0"
35
+ "lightnet": "^4.6.2"
35
36
  },
36
37
  "dependencies": {
37
- "@sveltia/cms": "0.208.2"
38
+ "@sveltia/cms": "0.248.0"
38
39
  },
39
40
  "devDependencies": {
40
41
  "@internal/e2e-test-utils": "^0.0.1",
41
- "@playwright/test": "^1.63.0",
42
+ "@playwright/test": "^1.64.1",
42
43
  "astro": "^6.4.8",
43
- "lightnet": "^4.6.0",
44
+ "lightnet": "^4.6.2",
44
45
  "typescript": "^6.0.3",
45
- "vitest": "^5.0.0"
46
+ "vitest": "^5.0.5"
46
47
  },
47
48
  "engines": {
48
49
  "node": ">=22"
49
50
  },
50
51
  "scripts": {
51
- "test": "vitest",
52
+ "test": "vitest run",
52
53
  "e2e": "playwright test"
53
54
  }
54
55
  }
package/src/Admin.astro CHANGED
@@ -8,6 +8,7 @@ import { pathWithBase } from "./utils/path-with-base"
8
8
  <html>
9
9
  <head>
10
10
  <meta charset="utf-8" />
11
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
11
12
  <meta name="robots" content="noindex" />
12
13
  <title>LightNet Administration</title>
13
14
  <link
@@ -19,6 +20,7 @@ import { pathWithBase } from "./utils/path-with-base"
19
20
  <body>
20
21
  <script>
21
22
  import { init } from "@sveltia/cms"
23
+
22
24
  init({})
23
25
  window.localStorage.setItem(
24
26
  "ln.adminPaths",
@@ -0,0 +1,50 @@
1
+ import { readFile } from "node:fs/promises"
2
+
3
+ import type { APIRoute } from "astro"
4
+ import config from "virtual:lightnet/sveltiaAdminConfig"
5
+
6
+ import { pathWithBase } from "./utils/path-with-base"
7
+
8
+ let sveltiaScriptUrl = ""
9
+ const getSveltiaScriptUrl = async () => {
10
+ // The package does not export package.json. Resolve its entry first so this
11
+ // follows the installed version, including pnpm overrides and workspace links.
12
+ if (sveltiaScriptUrl) {
13
+ return sveltiaScriptUrl
14
+ }
15
+ const cmsEntry = import.meta.resolve("@sveltia/cms")
16
+ const { version } = JSON.parse(
17
+ await readFile(new URL("../package.json", cmsEntry), "utf8"),
18
+ ) as { version: string }
19
+ sveltiaScriptUrl = `https://unpkg.com/@sveltia/cms@${version}/dist/sveltia-cms.mjs`
20
+ return sveltiaScriptUrl
21
+ }
22
+
23
+ export const GET: APIRoute = async () => {
24
+ const configUrl = pathWithBase(`/${config.path}/config.json`)
25
+ const scriptUrl = await getSveltiaScriptUrl()
26
+
27
+ return new Response(
28
+ `<!doctype html>
29
+ <html>
30
+ <head>
31
+ <meta charset="utf-8">
32
+ <meta name="viewport" content="width=device-width, initial-scale=1">
33
+ <meta name="robots" content="noindex">
34
+ <title>LightNet Administration</title>
35
+ <link href="${configUrl}" type="application/json" rel="cms-config-url">
36
+ </head>
37
+ <body>
38
+ <script type="module">
39
+ import CMS from ${JSON.stringify(scriptUrl)};
40
+ CMS.init({});
41
+ window.localStorage.setItem("ln.adminPaths", JSON.stringify({
42
+ rootPath: window.location.pathname,
43
+ mediaItemPath: window.location.pathname + "#/collections/media/entries/{{mediaId}}"
44
+ }));
45
+ </script>
46
+ </body>
47
+ </html>`,
48
+ { headers: { "Content-Type": "text/html; charset=utf-8" } },
49
+ )
50
+ }
@@ -54,11 +54,17 @@ const r2Storage = z.object({
54
54
 
55
55
  export const adminConfigSchema = z.object({
56
56
  /**
57
- * Path for the admin page.
57
+ * Nonempty path for the admin page. The site root is not supported.
58
+ * Provide a valid URL path with any special characters percent-encoded.
58
59
  *
59
- * Default is /admin
60
+ * Default is "admin"
60
61
  */
61
- path: z.string().default("admin").transform(normalizeAdminPath),
62
+ path: z
63
+ .string()
64
+ .trim()
65
+ .default("admin")
66
+ .transform(normalizeAdminPath)
67
+ .pipe(z.string().trim().min(1, "Admin path must not be empty")),
62
68
  /**
63
69
  * Maximum upload file size in megabytes.
64
70
  *
@@ -156,7 +162,7 @@ export const adminConfigSchema = z.object({
156
162
  */
157
163
  showDateCreatedField: z.boolean().default(true),
158
164
  /**
159
- * Show `commonI`d field in the media item editor.
165
+ * Show `commonId` field in the media item editor.
160
166
  *
161
167
  * @default true
162
168
  */
@@ -14,7 +14,7 @@ export default function lightnetSveltiaAdmin(
14
14
  return {
15
15
  name: "@lightnet/sveltia-admin",
16
16
  hooks: {
17
- "astro:config:setup": ({ injectRoute, updateConfig }) => {
17
+ "astro:config:setup": ({ command, injectRoute, updateConfig }) => {
18
18
  if (Object.hasOwn(config, "imagesFolder")) {
19
19
  throw new AstroError(
20
20
  "Invalid LightNet Administration UI configuration",
@@ -29,11 +29,23 @@ export default function lightnetSveltiaAdmin(
29
29
  "Fix these errors on the sveltiaAdmin configuration inside astro.config.mjs:",
30
30
  )
31
31
 
32
- injectRoute({
33
- pattern: preparedConfig.path,
34
- entrypoint: "@lightnet/sveltia-admin/Admin.astro",
35
- prerender: true,
36
- })
32
+ // During development, serve plain HTML to prevent Vite reloads from
33
+ // interrupting CMS saves. In production, use the Astro page to bundle assets.
34
+ // See: https://sveltiacms.app/en/docs/workflows/local#troubleshooting
35
+ if (command === "dev") {
36
+ injectRoute({
37
+ pattern: preparedConfig.path,
38
+ entrypoint: "@lightnet/sveltia-admin/admin.html.ts",
39
+ prerender: true,
40
+ })
41
+ } else {
42
+ injectRoute({
43
+ pattern: preparedConfig.path,
44
+ entrypoint: "@lightnet/sveltia-admin/Admin.astro",
45
+ prerender: true,
46
+ })
47
+ }
48
+
37
49
  injectRoute({
38
50
  pattern: `${preparedConfig.path}/config.json`,
39
51
  entrypoint: "@lightnet/sveltia-admin/config.json.ts",
@@ -1,7 +1,6 @@
1
1
  import type { Collection } from "@sveltia/cms"
2
2
  import adminConfig from "virtual:lightnet/sveltiaAdminConfig"
3
3
 
4
- import { projectPath } from "../utils/paths"
5
4
  import { inlineTranslation, translatedLabel } from "./fields/inline-translation"
6
5
 
7
6
  export const categoriesCollection: Collection = {
@@ -10,8 +9,8 @@ export const categoriesCollection: Collection = {
10
9
  description:
11
10
  "Use categories to group related media items by topic. Examples: discipleship, youth, prayer. [Read documentation](https://docs.lightnet.community/content/categories/)",
12
11
  label_singular: "Category",
13
- folder: projectPath("src/content/categories"),
14
- create: true,
12
+ folder: "src/content/categories",
13
+ create: !adminConfig.experimental.hideCategoriesCollection,
15
14
  hide: adminConfig.experimental.hideCategoriesCollection,
16
15
  format: "json",
17
16
  slug: adminConfig.experimental.showSlugField
@@ -2,7 +2,6 @@ import type { CollectionFile } from "@sveltia/cms"
2
2
  import config from "virtual:lightnet/config"
3
3
  import adminConfig from "virtual:lightnet/sveltiaAdminConfig"
4
4
 
5
- import { projectPath } from "../utils/paths"
6
5
  import { inlineTranslation } from "./fields/inline-translation"
7
6
 
8
7
  const { summaryLocale } = adminConfig.experimental
@@ -49,7 +48,7 @@ export const defineLanguagesCollection = () => {
49
48
  const languagesCollection: CollectionFile = {
50
49
  name: "languages",
51
50
  label: "Languages",
52
- file: projectPath("languages.json"),
51
+ file: "languages.json",
53
52
  format: "json",
54
53
  icon: "language",
55
54
  fields: [
@@ -1,7 +1,6 @@
1
1
  import type { Collection } from "@sveltia/cms"
2
2
  import adminConfig from "virtual:lightnet/sveltiaAdminConfig"
3
3
 
4
- import { projectPath } from "../utils/paths"
5
4
  import { inlineTranslation, translatedLabel } from "./fields/inline-translation"
6
5
 
7
6
  export const mediaCollectionCollection: Collection = {
@@ -10,9 +9,9 @@ export const mediaCollectionCollection: Collection = {
10
9
  description:
11
10
  "Use media collections to group related media items in order. Examples: a course, a series, a study path. [Read documentation](https://docs.lightnet.community/content/media-collections/)",
12
11
  label_singular: "Media Collection",
13
- create: true,
12
+ create: !adminConfig.experimental.hideMediaCollectionsCollection,
14
13
  hide: adminConfig.experimental.hideMediaCollectionsCollection,
15
- folder: projectPath("src/content/media-collections"),
14
+ folder: "src/content/media-collections",
16
15
  format: "json",
17
16
  slug: adminConfig.experimental.showSlugField
18
17
  ? "{{fields._slug}}"
@@ -3,7 +3,6 @@ import config from "virtual:lightnet/config"
3
3
  import adminConfig from "virtual:lightnet/sveltiaAdminConfig"
4
4
 
5
5
  import { isDefined } from "../utils/is-defined"
6
- import { projectPath } from "../utils/paths"
7
6
  import { inlineTranslation, translatedLabel } from "./fields/inline-translation"
8
7
  import { languagesSelect } from "./languages"
9
8
 
@@ -13,7 +12,7 @@ export const mediaItemCollection: Collection = {
13
12
  description:
14
13
  "Use media items to add resources people can open or download. Examples: a book PDF, a YouTube link. [Read documentation](https://docs.lightnet.community/content/media-items/)",
15
14
  label_singular: "Media Item",
16
- folder: projectPath("src/content/media"),
15
+ folder: "src/content/media",
17
16
  create: true,
18
17
  preview_path: `${config.defaultLocale}/media/{{filename}}`,
19
18
  format: "json",
@@ -175,7 +174,7 @@ function getFileStorage() {
175
174
  const externalFileStorage = adminConfig.experimental.fileStorage
176
175
  if (!externalFileStorage) {
177
176
  return {
178
- media_folder: projectPath("public/files"),
177
+ media_folder: "/public/files",
179
178
  public_folder: "/files",
180
179
  hint: `Upload a file up to ${adminConfig.maxFileSize} MB.`,
181
180
  media_libraries: {
@@ -1,7 +1,6 @@
1
1
  import type { Collection } from "@sveltia/cms"
2
2
  import adminConfig from "virtual:lightnet/sveltiaAdminConfig"
3
3
 
4
- import { projectPath } from "../utils/paths"
5
4
  import { inlineTranslation, translatedLabel } from "./fields/inline-translation"
6
5
 
7
6
  export const mediaTypeCollection: Collection = {
@@ -10,8 +9,9 @@ export const mediaTypeCollection: Collection = {
10
9
  description:
11
10
  "Use media types to organize media items by format. Examples: books, videos, audio. [Read documentation](https://docs.lightnet.community/content/media-types/)",
12
11
  label_singular: "Media Type",
13
- folder: projectPath("src/content/media-types"),
12
+ folder: "src/content/media-types",
14
13
  format: "json",
14
+ create: !adminConfig.experimental.hideMediaTypesCollection,
15
15
  hide: adminConfig.experimental.hideMediaTypesCollection,
16
16
  slug: adminConfig.experimental.showSlugField
17
17
  ? "{{fields._slug}}"
@@ -41,7 +41,7 @@ export const mediaTypeCollection: Collection = {
41
41
  required: false,
42
42
  typeKey: "layout",
43
43
  label: "Details Page Configuration",
44
- default: {},
44
+ default: { layout: "default" },
45
45
  widget: "object",
46
46
  types: [
47
47
  {
@@ -9,14 +9,13 @@ import { mediaCollectionCollection } from "./collections/media-collections"
9
9
  import { mediaItemCollection } from "./collections/media-items"
10
10
  import { mediaTypeCollection } from "./collections/media-types"
11
11
  import { isDefined } from "./utils/is-defined"
12
- import { projectPath } from "./utils/paths"
13
12
 
14
13
  export function createConfig(
15
14
  siteUrl = process.env.LIGHTNET_DEV_SITE_URL ?? site,
16
15
  ): CmsConfig {
17
16
  return {
18
17
  backend: getBackend(),
19
- media_folder: projectPath("src/assets"),
18
+ media_folder: "src/assets",
20
19
  public_folder: "/src/assets",
21
20
  app_title: "LightNet Admin",
22
21
  logo: {
@@ -73,9 +72,13 @@ const optimizedImage = {
73
72
 
74
73
  function getBackend(): Backend {
75
74
  const { backend } = adminConfig
75
+ const commonConfig = {
76
+ root_dir: adminConfig.siteRootInRepo || undefined,
77
+ }
76
78
 
77
79
  if (!backend) {
78
80
  return {
81
+ ...commonConfig,
79
82
  name: "github",
80
83
  repo: createLocalRepoPath(),
81
84
  }
@@ -83,6 +86,7 @@ function getBackend(): Backend {
83
86
 
84
87
  if (backend.name === "github") {
85
88
  return {
89
+ ...commonConfig,
86
90
  ...backend,
87
91
  base_url: backend.baseUrl,
88
92
  }
@@ -90,12 +94,13 @@ function getBackend(): Backend {
90
94
 
91
95
  if (backend.name === "gitlab") {
92
96
  return {
97
+ ...commonConfig,
93
98
  ...backend,
94
99
  app_id: backend.appId,
95
100
  auth_type: backend.authType,
96
101
  }
97
102
  }
98
- return backend
103
+ return { ...commonConfig, ...backend }
99
104
  }
100
105
 
101
106
  // Sveltia CMS uses repo as unique site identifier for IndexedDB
@@ -1,6 +0,0 @@
1
- import sveltiaAdminConfig from "virtual:lightnet/sveltiaAdminConfig"
2
-
3
- const basePath: string = sveltiaAdminConfig.siteRootInRepo
4
-
5
- export const projectPath = (path: string) =>
6
- `${basePath}${basePath.endsWith("/") || path.startsWith("/") ? "" : "/"}${path}`