site-config-stack 1.6.7 → 2.0.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/dist/index.cjs CHANGED
@@ -1,7 +1,44 @@
1
1
  'use strict';
2
2
 
3
+ const vue = require('vue');
3
4
  const ufo = require('ufo');
4
5
 
6
+ function normalizeSiteConfig(config) {
7
+ if (typeof config.indexable !== "undefined")
8
+ config.indexable = String(config.indexable) !== "false";
9
+ if (typeof config.trailingSlash !== "undefined" && !config.trailingSlash)
10
+ config.trailingSlash = String(config.trailingSlash) !== "false";
11
+ if (config.url && !ufo.hasProtocol(config.url, { acceptRelative: true, strict: false }))
12
+ config.url = ufo.withHttps(config.url);
13
+ const keys = Object.keys(config).sort((a, b) => a.localeCompare(b));
14
+ const newConfig = {};
15
+ for (const k of keys)
16
+ newConfig[k] = config[k];
17
+ return newConfig;
18
+ }
19
+ function validateSiteConfigStack(stack) {
20
+ const resolved = normalizeSiteConfig(stack.get({
21
+ // we need the context
22
+ debug: true
23
+ }));
24
+ const errors = [];
25
+ if (resolved.url) {
26
+ const val = resolved.url;
27
+ const context = resolved._context?.url || "unknown";
28
+ const url = ufo.parseURL(val);
29
+ if (!url.host)
30
+ errors.push(`url "${val}" from ${context} is not absolute`);
31
+ else if (url.pathname && url.pathname !== "/")
32
+ errors.push(`url "${val}" from ${context} should not contain a path`);
33
+ else if (url.hash)
34
+ errors.push(`url "${val}" from ${context} should not contain a hash`);
35
+ else if (Object.keys(ufo.getQuery(val)).length > 0)
36
+ errors.push(`url "${val}" from ${context} should not contain a query`);
37
+ else if (url.host === "localhost")
38
+ errors.push(`url "${val}" from ${context} should not be localhost`);
39
+ }
40
+ return errors;
41
+ }
5
42
  function createSiteConfigStack(options) {
6
43
  const debug = options?.debug || false;
7
44
  const stack = [];
@@ -32,13 +69,13 @@ function createSiteConfigStack(options) {
32
69
  const key = k;
33
70
  const val = stack[o][k];
34
71
  if (!k.startsWith("_")) {
35
- siteConfig[k] = val;
72
+ siteConfig[k] = options2?.resolveRefs ? vue.toValue(val) : val;
36
73
  if (options2?.debug)
37
74
  siteConfig._context[key] = stack[o]._context?.[key] || stack[o]._context || "anonymous";
38
75
  }
39
76
  }
40
77
  }
41
- return normalizeSiteConfig(siteConfig);
78
+ return options2?.skipNormalize ? siteConfig : normalizeSiteConfig(siteConfig);
42
79
  }
43
80
  return {
44
81
  stack,
@@ -47,44 +84,14 @@ function createSiteConfigStack(options) {
47
84
  };
48
85
  }
49
86
 
50
- function normalizeSiteConfig(config) {
51
- if (typeof config.indexable !== "undefined")
52
- config.indexable = String(config.indexable) !== "false";
53
- if (typeof config.trailingSlash !== "undefined")
54
- config.trailingSlash = String(config.trailingSlash) !== "false";
55
- if (config.url && !ufo.hasProtocol(config.url, { acceptRelative: true, strict: false }))
56
- config.url = ufo.withHttps(config.url);
57
- const keys = Object.keys(config).sort((a, b) => a.localeCompare(b));
58
- const newConfig = {};
59
- for (const k of keys)
60
- newConfig[k] = config[k];
61
- return newConfig;
62
- }
63
- function resolveSitePath(pathOrUrl, options) {
64
- let path = pathOrUrl;
65
- if (ufo.hasProtocol(pathOrUrl, { strict: false, acceptRelative: true })) {
66
- const parsed = ufo.parseURL(pathOrUrl);
67
- path = parsed.pathname;
68
- }
69
- const base = ufo.withLeadingSlash(options.base || "/");
70
- if (base !== "/" && path.startsWith(base)) {
71
- path = path.slice(base.length);
72
- }
73
- const origin = options.absolute ? options.siteUrl : "";
74
- const baseWithOrigin = options.withBase ? ufo.withBase(base, origin || "/") : origin;
75
- const resolvedUrl = ufo.withBase(path, baseWithOrigin);
76
- return path === "/" && !options.withBase ? ufo.withTrailingSlash(resolvedUrl) : fixSlashes(options.trailingSlash, resolvedUrl);
77
- }
78
- function fixSlashes(trailingSlash, pathOrUrl) {
79
- const $url = ufo.parseURL(pathOrUrl);
80
- const isFileUrl = $url.pathname.includes(".");
81
- if (isFileUrl)
82
- return pathOrUrl;
83
- const fixedPath = trailingSlash ? ufo.withTrailingSlash($url.pathname) : ufo.withoutTrailingSlash($url.pathname);
84
- return `${$url.protocol ? `${$url.protocol}//` : ""}${$url.host || ""}${fixedPath}${$url.search || ""}${$url.hash || ""}`;
87
+ function envSiteConfig(env) {
88
+ return Object.fromEntries(Object.entries(env).filter(([k]) => k.startsWith("NUXT_SITE_") || k.startsWith("NUXT_PUBLIC_SITE_")).map(([k, v]) => [
89
+ k.replace(/^NUXT_(PUBLIC_)?SITE_/, "").split("_").map((s, i) => i === 0 ? s.toLowerCase() : s[0].toUpperCase() + s.slice(1).toLowerCase()).join(""),
90
+ v
91
+ ]));
85
92
  }
86
93
 
87
94
  exports.createSiteConfigStack = createSiteConfigStack;
88
- exports.fixSlashes = fixSlashes;
95
+ exports.envSiteConfig = envSiteConfig;
89
96
  exports.normalizeSiteConfig = normalizeSiteConfig;
90
- exports.resolveSitePath = resolveSitePath;
97
+ exports.validateSiteConfigStack = validateSiteConfigStack;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ComputedRef, Ref } from 'vue';
2
2
 
3
- interface SiteConfig {
3
+ interface SiteConfigResolved {
4
4
  /**
5
5
  * The canonical Site URL.
6
6
  *
@@ -19,91 +19,62 @@ interface SiteConfig {
19
19
  *
20
20
  * Used by: nuxt-schema-org, nuxt-seo-kit
21
21
  */
22
- name: string;
22
+ name?: string;
23
23
  /**
24
24
  * Whether the site is indexable by search engines.
25
+ *
26
+ * @deprecated we should only rely on `env`
25
27
  */
26
- indexable: boolean;
28
+ indexable?: boolean;
27
29
  /**
28
30
  * The environment of the site. Comparable to `process.env.NODE_ENV`.
29
31
  */
30
- env: 'production' | 'staging' | 'development' | string;
32
+ env?: 'production' | 'staging' | 'development' | 'preview' | 'uat' | string;
31
33
  /**
32
34
  * Whether the site uses trailing slash.
33
35
  */
34
- trailingSlash: boolean;
35
- /**
36
- * Current locale, set with `@nuxt/i18n`.
37
- *
38
- * Falls back to the defaultLocale.
39
- */
40
- currentLocale?: string;
41
- /**
42
- * The default locale of the site.
43
- */
44
- defaultLocale: string;
45
- /**
46
- * The description of the site.
47
- *
48
- * @default `process.env.NUXT_PUBLIC_SITE_DESCRIPTION`
49
- *
50
- * Used by: nuxt-schema-org, nuxt-seo-kit
51
- */
52
- description?: string;
36
+ trailingSlash?: boolean;
53
37
  /**
54
- * Configure the identity of the site.
55
- */
56
- identity?: {
57
- /**
58
- * Use Organization for when the site is a company, business, etc.
59
- * Use Person for when the site is a personal blog, portfolio, etc.
60
- */
61
- type: 'Organization' | 'Person';
62
- };
63
- /**
64
- * Twitter (X) profile ID.
65
- *
66
- * Used for Schema.org sameAs and `<meta profile>`.
67
- *
68
- * @example `@harlan_zw`
38
+ * The mapping of the context of each site config value being set.
69
39
  */
70
- twitter?: string;
40
+ _context?: Record<string, string>;
71
41
  /**
72
- * The mapping of the context of each site config value being set.
42
+ * Support any keys as site config.
73
43
  */
74
- _context: Partial<Record<Exclude<keyof SiteConfig, '_meta'>, string>>;
75
- [key: (string & Record<never, never>)]: any;
44
+ [key: string]: any;
76
45
  }
46
+ /**
47
+ * @deprecated use SiteConfigResolved
48
+ */
49
+ type SiteConfig = SiteConfigResolved;
77
50
  type MaybeComputedRef<T> = T | (() => T) | ComputedRef<T> | Ref<T>;
78
51
  type MaybeComputedRefEntries<T> = {
79
52
  [key in keyof T]?: MaybeComputedRef<T[key]>;
80
53
  };
81
- type SiteConfigInput = Omit<MaybeComputedRefEntries<Partial<SiteConfig>>, '_context' | 'indexable'> & {
54
+ type SiteConfigInput = Omit<MaybeComputedRefEntries<Partial<SiteConfigResolved>>, '_context' | 'indexable'> & {
82
55
  _context?: string;
83
56
  _priority?: number;
84
57
  indexable?: MaybeComputedRef<string | boolean>;
85
58
  };
86
59
  interface GetSiteConfigOptions {
87
60
  debug?: boolean;
61
+ skipNormalize?: boolean;
62
+ resolveRefs?: boolean;
88
63
  }
89
64
  interface SiteConfigStack {
90
65
  stack: Partial<SiteConfigInput>[];
91
66
  push: (config: SiteConfigInput) => void;
92
- get: (options?: GetSiteConfigOptions) => SiteConfig;
67
+ get: (options?: GetSiteConfigOptions) => SiteConfigResolved;
93
68
  }
94
69
 
70
+ declare function normalizeSiteConfig(config: SiteConfigResolved): SiteConfigResolved;
71
+ declare function validateSiteConfigStack(stack: SiteConfigStack): string[];
95
72
  declare function createSiteConfigStack(options?: {
96
73
  debug: boolean;
97
74
  }): SiteConfigStack;
98
75
 
99
- declare function normalizeSiteConfig(config: SiteConfig): SiteConfig;
100
- declare function resolveSitePath(pathOrUrl: string, options: {
101
- siteUrl: string;
102
- trailingSlash: boolean;
103
- base?: string;
104
- absolute?: boolean;
105
- withBase?: boolean;
106
- }): string;
107
- declare function fixSlashes(trailingSlash: boolean, pathOrUrl: string): string;
76
+ declare function envSiteConfig(env: Record<string, any>): {
77
+ [k: string]: any;
78
+ };
108
79
 
109
- export { type GetSiteConfigOptions, type MaybeComputedRef, type MaybeComputedRefEntries, type SiteConfig, type SiteConfigInput, type SiteConfigStack, createSiteConfigStack, fixSlashes, normalizeSiteConfig, resolveSitePath };
80
+ export { type GetSiteConfigOptions, type MaybeComputedRef, type MaybeComputedRefEntries, type SiteConfig, type SiteConfigInput, type SiteConfigResolved, type SiteConfigStack, createSiteConfigStack, envSiteConfig, normalizeSiteConfig, validateSiteConfigStack };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ComputedRef, Ref } from 'vue';
2
2
 
3
- interface SiteConfig {
3
+ interface SiteConfigResolved {
4
4
  /**
5
5
  * The canonical Site URL.
6
6
  *
@@ -19,91 +19,62 @@ interface SiteConfig {
19
19
  *
20
20
  * Used by: nuxt-schema-org, nuxt-seo-kit
21
21
  */
22
- name: string;
22
+ name?: string;
23
23
  /**
24
24
  * Whether the site is indexable by search engines.
25
+ *
26
+ * @deprecated we should only rely on `env`
25
27
  */
26
- indexable: boolean;
28
+ indexable?: boolean;
27
29
  /**
28
30
  * The environment of the site. Comparable to `process.env.NODE_ENV`.
29
31
  */
30
- env: 'production' | 'staging' | 'development' | string;
32
+ env?: 'production' | 'staging' | 'development' | 'preview' | 'uat' | string;
31
33
  /**
32
34
  * Whether the site uses trailing slash.
33
35
  */
34
- trailingSlash: boolean;
35
- /**
36
- * Current locale, set with `@nuxt/i18n`.
37
- *
38
- * Falls back to the defaultLocale.
39
- */
40
- currentLocale?: string;
41
- /**
42
- * The default locale of the site.
43
- */
44
- defaultLocale: string;
45
- /**
46
- * The description of the site.
47
- *
48
- * @default `process.env.NUXT_PUBLIC_SITE_DESCRIPTION`
49
- *
50
- * Used by: nuxt-schema-org, nuxt-seo-kit
51
- */
52
- description?: string;
36
+ trailingSlash?: boolean;
53
37
  /**
54
- * Configure the identity of the site.
55
- */
56
- identity?: {
57
- /**
58
- * Use Organization for when the site is a company, business, etc.
59
- * Use Person for when the site is a personal blog, portfolio, etc.
60
- */
61
- type: 'Organization' | 'Person';
62
- };
63
- /**
64
- * Twitter (X) profile ID.
65
- *
66
- * Used for Schema.org sameAs and `<meta profile>`.
67
- *
68
- * @example `@harlan_zw`
38
+ * The mapping of the context of each site config value being set.
69
39
  */
70
- twitter?: string;
40
+ _context?: Record<string, string>;
71
41
  /**
72
- * The mapping of the context of each site config value being set.
42
+ * Support any keys as site config.
73
43
  */
74
- _context: Partial<Record<Exclude<keyof SiteConfig, '_meta'>, string>>;
75
- [key: (string & Record<never, never>)]: any;
44
+ [key: string]: any;
76
45
  }
46
+ /**
47
+ * @deprecated use SiteConfigResolved
48
+ */
49
+ type SiteConfig = SiteConfigResolved;
77
50
  type MaybeComputedRef<T> = T | (() => T) | ComputedRef<T> | Ref<T>;
78
51
  type MaybeComputedRefEntries<T> = {
79
52
  [key in keyof T]?: MaybeComputedRef<T[key]>;
80
53
  };
81
- type SiteConfigInput = Omit<MaybeComputedRefEntries<Partial<SiteConfig>>, '_context' | 'indexable'> & {
54
+ type SiteConfigInput = Omit<MaybeComputedRefEntries<Partial<SiteConfigResolved>>, '_context' | 'indexable'> & {
82
55
  _context?: string;
83
56
  _priority?: number;
84
57
  indexable?: MaybeComputedRef<string | boolean>;
85
58
  };
86
59
  interface GetSiteConfigOptions {
87
60
  debug?: boolean;
61
+ skipNormalize?: boolean;
62
+ resolveRefs?: boolean;
88
63
  }
89
64
  interface SiteConfigStack {
90
65
  stack: Partial<SiteConfigInput>[];
91
66
  push: (config: SiteConfigInput) => void;
92
- get: (options?: GetSiteConfigOptions) => SiteConfig;
67
+ get: (options?: GetSiteConfigOptions) => SiteConfigResolved;
93
68
  }
94
69
 
70
+ declare function normalizeSiteConfig(config: SiteConfigResolved): SiteConfigResolved;
71
+ declare function validateSiteConfigStack(stack: SiteConfigStack): string[];
95
72
  declare function createSiteConfigStack(options?: {
96
73
  debug: boolean;
97
74
  }): SiteConfigStack;
98
75
 
99
- declare function normalizeSiteConfig(config: SiteConfig): SiteConfig;
100
- declare function resolveSitePath(pathOrUrl: string, options: {
101
- siteUrl: string;
102
- trailingSlash: boolean;
103
- base?: string;
104
- absolute?: boolean;
105
- withBase?: boolean;
106
- }): string;
107
- declare function fixSlashes(trailingSlash: boolean, pathOrUrl: string): string;
76
+ declare function envSiteConfig(env: Record<string, any>): {
77
+ [k: string]: any;
78
+ };
108
79
 
109
- export { type GetSiteConfigOptions, type MaybeComputedRef, type MaybeComputedRefEntries, type SiteConfig, type SiteConfigInput, type SiteConfigStack, createSiteConfigStack, fixSlashes, normalizeSiteConfig, resolveSitePath };
80
+ export { type GetSiteConfigOptions, type MaybeComputedRef, type MaybeComputedRefEntries, type SiteConfig, type SiteConfigInput, type SiteConfigResolved, type SiteConfigStack, createSiteConfigStack, envSiteConfig, normalizeSiteConfig, validateSiteConfigStack };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ComputedRef, Ref } from 'vue';
2
2
 
3
- interface SiteConfig {
3
+ interface SiteConfigResolved {
4
4
  /**
5
5
  * The canonical Site URL.
6
6
  *
@@ -19,91 +19,62 @@ interface SiteConfig {
19
19
  *
20
20
  * Used by: nuxt-schema-org, nuxt-seo-kit
21
21
  */
22
- name: string;
22
+ name?: string;
23
23
  /**
24
24
  * Whether the site is indexable by search engines.
25
+ *
26
+ * @deprecated we should only rely on `env`
25
27
  */
26
- indexable: boolean;
28
+ indexable?: boolean;
27
29
  /**
28
30
  * The environment of the site. Comparable to `process.env.NODE_ENV`.
29
31
  */
30
- env: 'production' | 'staging' | 'development' | string;
32
+ env?: 'production' | 'staging' | 'development' | 'preview' | 'uat' | string;
31
33
  /**
32
34
  * Whether the site uses trailing slash.
33
35
  */
34
- trailingSlash: boolean;
35
- /**
36
- * Current locale, set with `@nuxt/i18n`.
37
- *
38
- * Falls back to the defaultLocale.
39
- */
40
- currentLocale?: string;
41
- /**
42
- * The default locale of the site.
43
- */
44
- defaultLocale: string;
45
- /**
46
- * The description of the site.
47
- *
48
- * @default `process.env.NUXT_PUBLIC_SITE_DESCRIPTION`
49
- *
50
- * Used by: nuxt-schema-org, nuxt-seo-kit
51
- */
52
- description?: string;
36
+ trailingSlash?: boolean;
53
37
  /**
54
- * Configure the identity of the site.
55
- */
56
- identity?: {
57
- /**
58
- * Use Organization for when the site is a company, business, etc.
59
- * Use Person for when the site is a personal blog, portfolio, etc.
60
- */
61
- type: 'Organization' | 'Person';
62
- };
63
- /**
64
- * Twitter (X) profile ID.
65
- *
66
- * Used for Schema.org sameAs and `<meta profile>`.
67
- *
68
- * @example `@harlan_zw`
38
+ * The mapping of the context of each site config value being set.
69
39
  */
70
- twitter?: string;
40
+ _context?: Record<string, string>;
71
41
  /**
72
- * The mapping of the context of each site config value being set.
42
+ * Support any keys as site config.
73
43
  */
74
- _context: Partial<Record<Exclude<keyof SiteConfig, '_meta'>, string>>;
75
- [key: (string & Record<never, never>)]: any;
44
+ [key: string]: any;
76
45
  }
46
+ /**
47
+ * @deprecated use SiteConfigResolved
48
+ */
49
+ type SiteConfig = SiteConfigResolved;
77
50
  type MaybeComputedRef<T> = T | (() => T) | ComputedRef<T> | Ref<T>;
78
51
  type MaybeComputedRefEntries<T> = {
79
52
  [key in keyof T]?: MaybeComputedRef<T[key]>;
80
53
  };
81
- type SiteConfigInput = Omit<MaybeComputedRefEntries<Partial<SiteConfig>>, '_context' | 'indexable'> & {
54
+ type SiteConfigInput = Omit<MaybeComputedRefEntries<Partial<SiteConfigResolved>>, '_context' | 'indexable'> & {
82
55
  _context?: string;
83
56
  _priority?: number;
84
57
  indexable?: MaybeComputedRef<string | boolean>;
85
58
  };
86
59
  interface GetSiteConfigOptions {
87
60
  debug?: boolean;
61
+ skipNormalize?: boolean;
62
+ resolveRefs?: boolean;
88
63
  }
89
64
  interface SiteConfigStack {
90
65
  stack: Partial<SiteConfigInput>[];
91
66
  push: (config: SiteConfigInput) => void;
92
- get: (options?: GetSiteConfigOptions) => SiteConfig;
67
+ get: (options?: GetSiteConfigOptions) => SiteConfigResolved;
93
68
  }
94
69
 
70
+ declare function normalizeSiteConfig(config: SiteConfigResolved): SiteConfigResolved;
71
+ declare function validateSiteConfigStack(stack: SiteConfigStack): string[];
95
72
  declare function createSiteConfigStack(options?: {
96
73
  debug: boolean;
97
74
  }): SiteConfigStack;
98
75
 
99
- declare function normalizeSiteConfig(config: SiteConfig): SiteConfig;
100
- declare function resolveSitePath(pathOrUrl: string, options: {
101
- siteUrl: string;
102
- trailingSlash: boolean;
103
- base?: string;
104
- absolute?: boolean;
105
- withBase?: boolean;
106
- }): string;
107
- declare function fixSlashes(trailingSlash: boolean, pathOrUrl: string): string;
76
+ declare function envSiteConfig(env: Record<string, any>): {
77
+ [k: string]: any;
78
+ };
108
79
 
109
- export { type GetSiteConfigOptions, type MaybeComputedRef, type MaybeComputedRefEntries, type SiteConfig, type SiteConfigInput, type SiteConfigStack, createSiteConfigStack, fixSlashes, normalizeSiteConfig, resolveSitePath };
80
+ export { type GetSiteConfigOptions, type MaybeComputedRef, type MaybeComputedRefEntries, type SiteConfig, type SiteConfigInput, type SiteConfigResolved, type SiteConfigStack, createSiteConfigStack, envSiteConfig, normalizeSiteConfig, validateSiteConfigStack };
package/dist/index.mjs CHANGED
@@ -1,5 +1,42 @@
1
- import { hasProtocol, withHttps, parseURL, withLeadingSlash, withBase, withTrailingSlash, withoutTrailingSlash } from 'ufo';
1
+ import { toValue } from 'vue';
2
+ import { hasProtocol, withHttps, parseURL, getQuery } from 'ufo';
2
3
 
4
+ function normalizeSiteConfig(config) {
5
+ if (typeof config.indexable !== "undefined")
6
+ config.indexable = String(config.indexable) !== "false";
7
+ if (typeof config.trailingSlash !== "undefined" && !config.trailingSlash)
8
+ config.trailingSlash = String(config.trailingSlash) !== "false";
9
+ if (config.url && !hasProtocol(config.url, { acceptRelative: true, strict: false }))
10
+ config.url = withHttps(config.url);
11
+ const keys = Object.keys(config).sort((a, b) => a.localeCompare(b));
12
+ const newConfig = {};
13
+ for (const k of keys)
14
+ newConfig[k] = config[k];
15
+ return newConfig;
16
+ }
17
+ function validateSiteConfigStack(stack) {
18
+ const resolved = normalizeSiteConfig(stack.get({
19
+ // we need the context
20
+ debug: true
21
+ }));
22
+ const errors = [];
23
+ if (resolved.url) {
24
+ const val = resolved.url;
25
+ const context = resolved._context?.url || "unknown";
26
+ const url = parseURL(val);
27
+ if (!url.host)
28
+ errors.push(`url "${val}" from ${context} is not absolute`);
29
+ else if (url.pathname && url.pathname !== "/")
30
+ errors.push(`url "${val}" from ${context} should not contain a path`);
31
+ else if (url.hash)
32
+ errors.push(`url "${val}" from ${context} should not contain a hash`);
33
+ else if (Object.keys(getQuery(val)).length > 0)
34
+ errors.push(`url "${val}" from ${context} should not contain a query`);
35
+ else if (url.host === "localhost")
36
+ errors.push(`url "${val}" from ${context} should not be localhost`);
37
+ }
38
+ return errors;
39
+ }
3
40
  function createSiteConfigStack(options) {
4
41
  const debug = options?.debug || false;
5
42
  const stack = [];
@@ -30,13 +67,13 @@ function createSiteConfigStack(options) {
30
67
  const key = k;
31
68
  const val = stack[o][k];
32
69
  if (!k.startsWith("_")) {
33
- siteConfig[k] = val;
70
+ siteConfig[k] = options2?.resolveRefs ? toValue(val) : val;
34
71
  if (options2?.debug)
35
72
  siteConfig._context[key] = stack[o]._context?.[key] || stack[o]._context || "anonymous";
36
73
  }
37
74
  }
38
75
  }
39
- return normalizeSiteConfig(siteConfig);
76
+ return options2?.skipNormalize ? siteConfig : normalizeSiteConfig(siteConfig);
40
77
  }
41
78
  return {
42
79
  stack,
@@ -45,41 +82,11 @@ function createSiteConfigStack(options) {
45
82
  };
46
83
  }
47
84
 
48
- function normalizeSiteConfig(config) {
49
- if (typeof config.indexable !== "undefined")
50
- config.indexable = String(config.indexable) !== "false";
51
- if (typeof config.trailingSlash !== "undefined")
52
- config.trailingSlash = String(config.trailingSlash) !== "false";
53
- if (config.url && !hasProtocol(config.url, { acceptRelative: true, strict: false }))
54
- config.url = withHttps(config.url);
55
- const keys = Object.keys(config).sort((a, b) => a.localeCompare(b));
56
- const newConfig = {};
57
- for (const k of keys)
58
- newConfig[k] = config[k];
59
- return newConfig;
60
- }
61
- function resolveSitePath(pathOrUrl, options) {
62
- let path = pathOrUrl;
63
- if (hasProtocol(pathOrUrl, { strict: false, acceptRelative: true })) {
64
- const parsed = parseURL(pathOrUrl);
65
- path = parsed.pathname;
66
- }
67
- const base = withLeadingSlash(options.base || "/");
68
- if (base !== "/" && path.startsWith(base)) {
69
- path = path.slice(base.length);
70
- }
71
- const origin = options.absolute ? options.siteUrl : "";
72
- const baseWithOrigin = options.withBase ? withBase(base, origin || "/") : origin;
73
- const resolvedUrl = withBase(path, baseWithOrigin);
74
- return path === "/" && !options.withBase ? withTrailingSlash(resolvedUrl) : fixSlashes(options.trailingSlash, resolvedUrl);
75
- }
76
- function fixSlashes(trailingSlash, pathOrUrl) {
77
- const $url = parseURL(pathOrUrl);
78
- const isFileUrl = $url.pathname.includes(".");
79
- if (isFileUrl)
80
- return pathOrUrl;
81
- const fixedPath = trailingSlash ? withTrailingSlash($url.pathname) : withoutTrailingSlash($url.pathname);
82
- return `${$url.protocol ? `${$url.protocol}//` : ""}${$url.host || ""}${fixedPath}${$url.search || ""}${$url.hash || ""}`;
85
+ function envSiteConfig(env) {
86
+ return Object.fromEntries(Object.entries(env).filter(([k]) => k.startsWith("NUXT_SITE_") || k.startsWith("NUXT_PUBLIC_SITE_")).map(([k, v]) => [
87
+ k.replace(/^NUXT_(PUBLIC_)?SITE_/, "").split("_").map((s, i) => i === 0 ? s.toLowerCase() : s[0].toUpperCase() + s.slice(1).toLowerCase()).join(""),
88
+ v
89
+ ]));
83
90
  }
84
91
 
85
- export { createSiteConfigStack, fixSlashes, normalizeSiteConfig, resolveSitePath };
92
+ export { createSiteConfigStack, envSiteConfig, normalizeSiteConfig, validateSiteConfigStack };
package/dist/urls.cjs ADDED
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ const ufo = require('ufo');
4
+
5
+ function resolveSitePath(pathOrUrl, options) {
6
+ let path = pathOrUrl;
7
+ if (ufo.hasProtocol(pathOrUrl, { strict: false, acceptRelative: true })) {
8
+ const parsed = ufo.parseURL(pathOrUrl);
9
+ path = parsed.pathname;
10
+ }
11
+ const base = ufo.withLeadingSlash(options.base || "/");
12
+ if (base !== "/" && path.startsWith(base)) {
13
+ path = path.slice(base.length);
14
+ }
15
+ const origin = options.absolute ? options.siteUrl : "";
16
+ const baseWithOrigin = options.withBase ? ufo.withBase(base, origin || "/") : origin;
17
+ const resolvedUrl = ufo.withBase(path, baseWithOrigin);
18
+ return path === "/" && !options.withBase ? ufo.withTrailingSlash(resolvedUrl) : fixSlashes(options.trailingSlash, resolvedUrl);
19
+ }
20
+ function fixSlashes(trailingSlash, pathOrUrl) {
21
+ const $url = ufo.parseURL(pathOrUrl);
22
+ const isFileUrl = $url.pathname.includes(".");
23
+ if (isFileUrl)
24
+ return pathOrUrl;
25
+ const fixedPath = trailingSlash ? ufo.withTrailingSlash($url.pathname) : ufo.withoutTrailingSlash($url.pathname);
26
+ return `${$url.protocol ? `${$url.protocol}//` : ""}${$url.host || ""}${fixedPath}${$url.search || ""}${$url.hash || ""}`;
27
+ }
28
+
29
+ exports.fixSlashes = fixSlashes;
30
+ exports.resolveSitePath = resolveSitePath;
@@ -0,0 +1,10 @@
1
+ declare function resolveSitePath(pathOrUrl: string, options: {
2
+ siteUrl: string;
3
+ trailingSlash?: boolean;
4
+ base?: string;
5
+ absolute?: boolean;
6
+ withBase?: boolean;
7
+ }): string;
8
+ declare function fixSlashes(trailingSlash: boolean | undefined, pathOrUrl: string): string;
9
+
10
+ export { fixSlashes, resolveSitePath };
@@ -0,0 +1,10 @@
1
+ declare function resolveSitePath(pathOrUrl: string, options: {
2
+ siteUrl: string;
3
+ trailingSlash?: boolean;
4
+ base?: string;
5
+ absolute?: boolean;
6
+ withBase?: boolean;
7
+ }): string;
8
+ declare function fixSlashes(trailingSlash: boolean | undefined, pathOrUrl: string): string;
9
+
10
+ export { fixSlashes, resolveSitePath };
package/dist/urls.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ declare function resolveSitePath(pathOrUrl: string, options: {
2
+ siteUrl: string;
3
+ trailingSlash?: boolean;
4
+ base?: string;
5
+ absolute?: boolean;
6
+ withBase?: boolean;
7
+ }): string;
8
+ declare function fixSlashes(trailingSlash: boolean | undefined, pathOrUrl: string): string;
9
+
10
+ export { fixSlashes, resolveSitePath };
package/dist/urls.mjs ADDED
@@ -0,0 +1,27 @@
1
+ import { hasProtocol, parseURL, withLeadingSlash, withBase, withTrailingSlash, withoutTrailingSlash } from 'ufo';
2
+
3
+ function resolveSitePath(pathOrUrl, options) {
4
+ let path = pathOrUrl;
5
+ if (hasProtocol(pathOrUrl, { strict: false, acceptRelative: true })) {
6
+ const parsed = parseURL(pathOrUrl);
7
+ path = parsed.pathname;
8
+ }
9
+ const base = withLeadingSlash(options.base || "/");
10
+ if (base !== "/" && path.startsWith(base)) {
11
+ path = path.slice(base.length);
12
+ }
13
+ const origin = options.absolute ? options.siteUrl : "";
14
+ const baseWithOrigin = options.withBase ? withBase(base, origin || "/") : origin;
15
+ const resolvedUrl = withBase(path, baseWithOrigin);
16
+ return path === "/" && !options.withBase ? withTrailingSlash(resolvedUrl) : fixSlashes(options.trailingSlash, resolvedUrl);
17
+ }
18
+ function fixSlashes(trailingSlash, pathOrUrl) {
19
+ const $url = parseURL(pathOrUrl);
20
+ const isFileUrl = $url.pathname.includes(".");
21
+ if (isFileUrl)
22
+ return pathOrUrl;
23
+ const fixedPath = trailingSlash ? withTrailingSlash($url.pathname) : withoutTrailingSlash($url.pathname);
24
+ return `${$url.protocol ? `${$url.protocol}//` : ""}${$url.host || ""}${fixedPath}${$url.search || ""}${$url.hash || ""}`;
25
+ }
26
+
27
+ export { fixSlashes, resolveSitePath };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "site-config-stack",
3
3
  "type": "module",
4
- "version": "1.6.7",
4
+ "version": "2.0.0",
5
5
  "description": "Shared site configuration utilities.",
6
6
  "license": "MIT",
7
7
  "funding": "https://github.com/sponsors/harlan-zw",
@@ -19,6 +19,16 @@
19
19
  "types": "./dist/index.d.ts",
20
20
  "import": "./dist/index.mjs",
21
21
  "require": "./dist/index.cjs"
22
+ },
23
+ "./urls": {
24
+ "types": "./dist/urls.d.ts",
25
+ "import": "./dist/urls.mjs",
26
+ "require": "./dist/urls.cjs"
27
+ },
28
+ "./dist/urls": {
29
+ "types": "./dist/urls.d.ts",
30
+ "import": "./dist/urls.mjs",
31
+ "require": "./dist/urls.cjs"
22
32
  }
23
33
  },
24
34
  "main": "./dist/index.cjs",
@@ -38,6 +48,7 @@
38
48
  "scripts": {
39
49
  "lint": "eslint . --fix",
40
50
  "build": "unbuild",
41
- "stub": "unbuild --stub"
51
+ "stub": "unbuild --stub",
52
+ "typecheck": "tsc --noEmit"
42
53
  }
43
54
  }