@orderlyshop/web-components 0.1.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.
Files changed (70) hide show
  1. package/AGENTS.md +110 -0
  2. package/README.md +685 -0
  3. package/bin/orderly-build-category-pages.mjs +160 -0
  4. package/bin/orderly-generate-category-pages.mjs +308 -0
  5. package/bin/orderly-hydrate-static-pages.mjs +595 -0
  6. package/bin/orderly-init-navigation.mjs +327 -0
  7. package/bin/orderly-init-shop.mjs +876 -0
  8. package/bin/orderly-init-taxonomy.mjs +2 -0
  9. package/bin/orderly-publish-site.mjs +342 -0
  10. package/custom-elements.json +495 -0
  11. package/dist/browser/orderly-web-components.define.global.js +3085 -0
  12. package/dist/browser/orderly-web-components.define.global.js.map +1 -0
  13. package/dist/browser/orderly-web-components.global.js +3085 -0
  14. package/dist/browser/orderly-web-components.global.js.map +1 -0
  15. package/dist/default-shop-DWdB_MRd.d.ts +220 -0
  16. package/dist/default-shop.d.ts +6 -0
  17. package/dist/default-shop.js +762 -0
  18. package/dist/default-shop.js.map +1 -0
  19. package/dist/define-IAQk8OmQ.d.ts +9 -0
  20. package/dist/define.d.ts +2 -0
  21. package/dist/define.js +10266 -0
  22. package/dist/define.js.map +1 -0
  23. package/dist/index.d.ts +683 -0
  24. package/dist/index.js +10589 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/navigation.d.ts +51 -0
  27. package/dist/navigation.js +818 -0
  28. package/dist/navigation.js.map +1 -0
  29. package/dist/query.d.ts +31 -0
  30. package/dist/query.js +115 -0
  31. package/dist/query.js.map +1 -0
  32. package/dist/registry-CPDecU3g.d.ts +6 -0
  33. package/dist/shop-BnT1C6kG.d.ts +173 -0
  34. package/dist/shop-query.d.ts +8 -0
  35. package/dist/shop-query.js +100 -0
  36. package/dist/shop-query.js.map +1 -0
  37. package/dist/shop.d.ts +8 -0
  38. package/dist/shop.js +10359 -0
  39. package/dist/shop.js.map +1 -0
  40. package/dist/stores.d.ts +46 -0
  41. package/dist/stores.js +145 -0
  42. package/dist/stores.js.map +1 -0
  43. package/dist/taxonomy.d.ts +35 -0
  44. package/dist/taxonomy.js +247 -0
  45. package/dist/taxonomy.js.map +1 -0
  46. package/dist/types-CCQDd6Nd.d.ts +95 -0
  47. package/docs/components/README.md +610 -0
  48. package/docs/components/product-grid.md +176 -0
  49. package/docs/components/product-rail.md +174 -0
  50. package/examples/shop/README.md +71 -0
  51. package/examples/shop/package.json +28 -0
  52. package/examples/shop/src/category.html +20 -0
  53. package/examples/shop/src/checkout.html +21 -0
  54. package/examples/shop/src/forretningsbetingelser.html +80 -0
  55. package/examples/shop/src/includes/body-end.html +1 -0
  56. package/examples/shop/src/includes/body-start.html +2 -0
  57. package/examples/shop/src/includes/head.html +32 -0
  58. package/examples/shop/src/index.html +25 -0
  59. package/examples/shop/src/navigation.ts +154 -0
  60. package/examples/shop/src/product.html +24 -0
  61. package/examples/shop/src/templates/shop-footer.html +76 -0
  62. package/examples/shop/tsconfig.json +32 -0
  63. package/examples/shop/vite.config.mjs +184 -0
  64. package/html-custom-data.json +262 -0
  65. package/package.json +118 -0
  66. package/server/README.md +111 -0
  67. package/server/apache/.htaccess +18 -0
  68. package/server/nginx/orderly-products.conf +24 -0
  69. package/server/node/product-snapshot-server.mjs +133 -0
  70. package/server/php/orderly-product.php +204 -0
@@ -0,0 +1,154 @@
1
+ import type { NavigationDefinition } from "@orderlyshop/web-components";
2
+
3
+ export const navigationDefinitions: NavigationDefinition[] = [
4
+ {
5
+ label: "Tøj & Mode",
6
+ slug: "toej-og-mode",
7
+ heroImage: "https://images.unsplash.com/photo-1496747611176-843222e1e57c?auto=format&fit=crop&w=1800&q=80",
8
+ query: { tags: ["børnetøj"] },
9
+ children: [
10
+ { label: "Dame", slug: "dame", query: { query: "dame tøj kjoler jakker mode" } },
11
+ { label: "Herre", slug: "herre", query: { query: "herre tøj skjorter jakker mode" } },
12
+ { label: "Børn & Baby", slug: "boern-og-baby", query: { query: "børn baby tøj" } },
13
+ { label: "Sport & Activewear", slug: "sport-og-activewear", query: { query: "sport activewear træning tøj" } },
14
+ { label: "Accessories", slug: "accessories", query: { query: "accessories tasker bælter mode" } }
15
+ ]
16
+ },
17
+ {
18
+ label: "Sko",
19
+ slug: "sko",
20
+ heroImage: "https://images.unsplash.com/photo-1542291026-7eec264c27ff?auto=format&fit=crop&w=1800&q=80",
21
+ query: { query: "Nike sko sneakers" },
22
+ children: [
23
+ { label: "Dame sko", slug: "dame-sko", query: { query: "dame sko sneakers støvler" } },
24
+ { label: "Herre sko", slug: "herre-sko", query: { query: "herre sko sneakers støvler" } },
25
+ { label: "Børnesko", slug: "boernesko", query: { query: "børn sko sneakers sandaler" } },
26
+ { label: "Sportssko", slug: "sportssko", query: { query: "sportssko løbesko træning" } },
27
+ { label: "Special", slug: "special", query: { query: "special sko sikkerhedssko vandresko" } }
28
+ ]
29
+ },
30
+ {
31
+ label: "Bolig & Indretning",
32
+ slug: "bolig-og-indretning",
33
+ heroImage: "https://images.unsplash.com/photo-1618221195710-dd6b41faaea6?auto=format&fit=crop&w=1800&q=80",
34
+ query: { tags: ["bolig"] },
35
+ children: [
36
+ { label: "Møbler", slug: "moebler", query: { query: "møbler sofa stol bord" } },
37
+ { label: "Belysning", slug: "belysning", query: { query: "lampe belysning bolig" } },
38
+ { label: "Tekstiler", slug: "tekstiler", query: { query: "tekstiler puder tæpper gardiner" } },
39
+ { label: "Køkkenudstyr", slug: "koekkenudstyr", query: { query: "køkkenudstyr service glas gryder" } },
40
+ { label: "Dekoration & Kunst", slug: "dekoration-og-kunst", query: { query: "kunst dekoration bolig design" } }
41
+ ]
42
+ },
43
+ {
44
+ label: "Elektronik",
45
+ slug: "elektronik",
46
+ heroImage: "https://images.unsplash.com/photo-1518770660439-4636190af475?auto=format&fit=crop&w=1800&q=80",
47
+ query: { tags: ["elektronik"] },
48
+ children: [
49
+ { label: "Computere & Tablets", slug: "computere-og-tablets", query: { query: "computer tablet laptop" } },
50
+ { label: "Mobil & Tilbehør", slug: "mobil-og-tilbehoer", query: { query: "mobil telefon cover oplader" } },
51
+ { label: "TV & Lyd", slug: "tv-og-lyd", query: { query: "tv lyd højtaler headset" } },
52
+ { label: "Gaming", slug: "gaming", query: { query: "gaming konsol controller spil" } },
53
+ { label: "Smart Home", slug: "smart-home", query: { query: "smart home lys sensor kamera" } }
54
+ ]
55
+ },
56
+ {
57
+ label: "Børn & Legetøj",
58
+ slug: "boern-og-legetoej",
59
+ heroImage: "https://images.unsplash.com/photo-1515488042361-ee00e0ddd4e4?auto=format&fit=crop&w=1800&q=80",
60
+ query: { tags: ["legetøj "] },
61
+ children: [
62
+ { label: "Legetøj", slug: "legetoej", query: { query: "legetøj bamser figurer spil" } },
63
+ { label: "Babyudstyr", slug: "babyudstyr", query: { query: "babyudstyr barnevogn autostol" } },
64
+ { label: "Børnemøbler", slug: "boernemoebler", query: { query: "børnemøbler seng stol bord" } },
65
+ { label: "Udendørs leg", slug: "udendoers-leg", query: { query: "udendørs leg trampolin sandkasse" } },
66
+ { label: "Læring & Kreativitet", slug: "laering-og-kreativitet", query: { query: "læring kreativitet hobby børn" } }
67
+ ]
68
+ },
69
+ {
70
+ label: "Sport & Fritid",
71
+ slug: "sport-og-fritid",
72
+ heroImage: "https://images.unsplash.com/photo-1517649763962-0c623066013b?auto=format&fit=crop&w=1800&q=80",
73
+ query: { tags: ["sport-fritid"] },
74
+ children: [
75
+ { label: "Fitness & Træning", slug: "fitness-og-traening", query: { query: "fitness træning sport" } },
76
+ { label: "Outdoor & Camping", slug: "outdoor-og-camping", query: { query: "outdoor camping vandring telt" } },
77
+ { label: "Cykler & Tilbehør", slug: "cykler-og-tilbehoer", query: { query: "cykler cykelhjelm cykeltilbehør" } },
78
+ { label: "Vandsport", slug: "vandsport", query: { query: "vandsport svømning paddleboard" } },
79
+ { label: "Hobby & DIY", slug: "hobby-og-diy", query: { query: "hobby diy værktøj kreativ" } }
80
+ ]
81
+ },
82
+ {
83
+ label: "Skønhed & Personlig Pleje",
84
+ slug: "skoenhed-og-personlig-pleje",
85
+ heroImage: "https://images.unsplash.com/photo-1522335789203-aabd1fc54bc9?auto=format&fit=crop&w=1800&q=80",
86
+ query: { query: "skønhed makeup hudpleje" },
87
+ children: [
88
+ { label: "Hudpleje", slug: "hudpleje", query: { query: "hudpleje creme serum rens" } },
89
+ { label: "Hårpleje", slug: "haarpleje", query: { query: "hårpleje shampoo balsam styling" } },
90
+ { label: "Makeup", slug: "makeup", query: { query: "makeup kosmetik skønhed" } },
91
+ { label: "Parfume", slug: "parfume", query: { query: "parfume duft fragrance" } },
92
+ { label: "Personlig pleje", slug: "personlig-pleje", query: { query: "personlig pleje barbering hygiene" } }
93
+ ]
94
+ },
95
+ {
96
+ label: "Smykker & Ure",
97
+ slug: "smykker-og-ure",
98
+ heroImage: "https://images.unsplash.com/photo-1515562141207-7a88fb7ce338?auto=format&fit=crop&w=1800&q=80",
99
+ query: { query: "smykker ure accessories" },
100
+ children: [
101
+ { label: "Smykker", slug: "smykker", query: { query: "smykker ring halskæde armbånd" } },
102
+ { label: "Ure", slug: "ure", query: { query: "ure smartwatch klassisk ur" } },
103
+ { label: "Luksusvarer", slug: "luksusvarer", query: { query: "luksusvarer designer premium" } }
104
+ ]
105
+ },
106
+ {
107
+ label: "Bøger, Medier & Samlerobjekter",
108
+ slug: "boeger-medier-og-samlerobjekter",
109
+ heroImage: "https://images.unsplash.com/photo-1524995997946-a1c2e315a42f?auto=format&fit=crop&w=1800&q=80",
110
+ query: { query: "bøger medier samlerobjekter" },
111
+ children: [
112
+ { label: "Bøger", slug: "boeger", query: { query: "bøger roman fagbog børnebog" } },
113
+ { label: "Film & Musik", slug: "film-og-musik", query: { query: "film musik dvd vinyl" } },
114
+ { label: "Spil", slug: "spil", query: { query: "spil brætspil konsolspil" } },
115
+ { label: "Samlerobjekter", slug: "samlerobjekter", query: { query: "samlerobjekter figurer vintage" } }
116
+ ]
117
+ },
118
+ {
119
+ label: "Bil & Transport",
120
+ slug: "bil-og-transport",
121
+ heroImage: "https://images.unsplash.com/photo-1503376780353-7e6692767b70?auto=format&fit=crop&w=1800&q=80",
122
+ query: { query: "bil transport tilbehør" },
123
+ children: [
124
+ { label: "Reservedele", slug: "reservedele", query: { query: "reservedele bil dele" } },
125
+ { label: "Tilbehør", slug: "tilbehoer", query: { query: "biltilbehør måtter holder" } },
126
+ { label: "Cykler & el-løbehjul", slug: "cykler-og-el-loebehjul", query: { query: "cykler el-løbehjul transport" } },
127
+ { label: "Autopleje", slug: "autopleje", query: { query: "autopleje voks rengøring bil" } }
128
+ ]
129
+ },
130
+ {
131
+ label: "Kontor & Erhverv",
132
+ slug: "kontor-og-erhverv",
133
+ heroImage: "https://images.unsplash.com/photo-1497366754035-f200968a6e72?auto=format&fit=crop&w=1800&q=80",
134
+ query: { query: "kontor erhverv udstyr" },
135
+ children: [
136
+ { label: "Kontorudstyr", slug: "kontorudstyr", query: { query: "kontorudstyr papir printer stol" } },
137
+ { label: "Lager & logistik", slug: "lager-og-logistik", query: { query: "lager logistik reol emballage" } },
138
+ { label: "Butiksinventar", slug: "butiksinventar", query: { query: "butiksinventar display kasseapparat" } },
139
+ { label: "Industrielt udstyr", slug: "industrielt-udstyr", query: { query: "industrielt udstyr maskiner værktøj" } }
140
+ ]
141
+ },
142
+ {
143
+ label: "Diverse & Specialvarer",
144
+ slug: "diverse-og-specialvarer",
145
+ heroImage: "https://images.unsplash.com/photo-1516975080664-ed2fc6a32937?auto=format&fit=crop&w=1800&q=80",
146
+ query: { query: "vintage antik specialvarer" },
147
+ children: [
148
+ { label: "Vintage & Antik", slug: "vintage-og-antik", query: { query: "vintage antik retro" } },
149
+ { label: "Restpartier", slug: "restpartier", query: { query: "restpartier outlet tilbud" } },
150
+ { label: "Upcycle materialer", slug: "upcycle-materialer", query: { query: "upcycle materialer genbrug" } },
151
+ { label: "Ukategoriseret", slug: "ukategoriseret", query: { query: "ukategoriseret diverse" } }
152
+ ]
153
+ }
154
+ ];
@@ -0,0 +1,24 @@
1
+ <!doctype html>
2
+ <html lang="da">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Produkt | Orderly Example Shop</title>
7
+ <!-- orderly-head-includes -->
8
+ </head>
9
+ <body>
10
+ <!-- orderly-body-start-includes -->
11
+ <orderly-product-detail-page
12
+ add-label="Læg i kurven"
13
+ loading-label="Indlæser produkt..."
14
+ not-found-label="Produktet blev ikke fundet."
15
+ error-label="Produktet kunne ikke indlæses.">
16
+ <div slot="utility" class="shop-utility-strip" aria-label="Kundefordele">
17
+ <span>Gratis levering over 499 kr.</span>
18
+ <span>30 dages returret</span>
19
+ <span>Sikker betaling</span>
20
+ </div>
21
+ </orderly-product-detail-page>
22
+ <!-- orderly-body-end-includes -->
23
+ </body>
24
+ </html>
@@ -0,0 +1,76 @@
1
+ <template data-orderly-template="footer" data-orderly-for="shop-footer">
2
+ <div class="orderly-shop-footer" aria-label="Sidefod">
3
+ <div class="orderly-shop-footer__brand">
4
+ <a class="orderly-shop-footer__logo" href="/" aria-label="Orderly Example Shop">
5
+ <img class="orderly-shop-footer__logo-image" src="https://orderly.shop/home/App_Icon.svg" alt="Orderly Example Shop">
6
+ </a>
7
+ <p class="orderly-shop-footer__about">
8
+ En demo webshop bygget med Orderly web components. Indholdet er eksempeldata, så layout, navigation, kurv og checkout kan prøves uden en shopspecifik frontend-stack.
9
+ </p>
10
+ </div>
11
+
12
+ <section class="orderly-shop-footer__section" aria-labelledby="exampleFooterAddress">
13
+ <h2 id="exampleFooterAddress" class="orderly-shop-footer__heading">Adresse</h2>
14
+ <address class="orderly-shop-footer__address">
15
+ <span>Orderly Example Shop ApS</span>
16
+ <span>Demo Allé 12</span>
17
+ <span>8000 Aarhus C</span>
18
+ <span>Danmark</span>
19
+ </address>
20
+ </section>
21
+
22
+ <section class="orderly-shop-footer__section" aria-labelledby="exampleFooterContact">
23
+ <h2 id="exampleFooterContact" class="orderly-shop-footer__heading">Kontakt</h2>
24
+ <ul class="orderly-shop-footer__list">
25
+ <li class="orderly-shop-footer__contact-item">
26
+ <span class="orderly-shop-footer__contact-label">Email</span>
27
+ <a class="orderly-shop-footer__contact-value" href="mailto:hello@example.orderly.shop">hello@example.orderly.shop</a>
28
+ </li>
29
+ <li class="orderly-shop-footer__contact-item">
30
+ <span class="orderly-shop-footer__contact-label">Telefon</span>
31
+ <a class="orderly-shop-footer__contact-value" href="tel:+4512345678">+45 12 34 56 78</a>
32
+ </li>
33
+ <li class="orderly-shop-footer__contact-item">
34
+ <span class="orderly-shop-footer__contact-label">Kundeservice</span>
35
+ <a class="orderly-shop-footer__contact-value" href="https://support.example.orderly.shop">support.example.orderly.shop</a>
36
+ </li>
37
+ </ul>
38
+ </section>
39
+
40
+ <section class="orderly-shop-footer__section" aria-labelledby="exampleFooterHours">
41
+ <h2 id="exampleFooterHours" class="orderly-shop-footer__heading">Åbningstider</h2>
42
+ <dl class="orderly-shop-footer__hours">
43
+ <div class="orderly-shop-footer__hours-row">
44
+ <dt class="orderly-shop-footer__hours-label">Mandag - fredag</dt>
45
+ <dd class="orderly-shop-footer__hours-value">09.00 - 17.00</dd>
46
+ </div>
47
+ <div class="orderly-shop-footer__hours-row">
48
+ <dt class="orderly-shop-footer__hours-label">Lørdag</dt>
49
+ <dd class="orderly-shop-footer__hours-value">10.00 - 14.00</dd>
50
+ </div>
51
+ <div class="orderly-shop-footer__hours-row">
52
+ <dt class="orderly-shop-footer__hours-label">Søndag</dt>
53
+ <dd class="orderly-shop-footer__hours-value">Lukket</dd>
54
+ </div>
55
+ </dl>
56
+ </section>
57
+
58
+ <section class="orderly-shop-footer__section" aria-labelledby="exampleFooterInformation">
59
+ <h2 id="exampleFooterInformation" class="orderly-shop-footer__heading">Information</h2>
60
+ <ul class="orderly-shop-footer__list">
61
+ <li class="orderly-shop-footer__link-item">
62
+ <a class="orderly-shop-footer__link" href="/forretningsbetingelser.html">Forretningsbetingelser</a>
63
+ </li>
64
+ <li class="orderly-shop-footer__link-item">
65
+ <a class="orderly-shop-footer__link" href="/information/levering-og-retur/">Levering og retur</a>
66
+ </li>
67
+ <li class="orderly-shop-footer__link-item">
68
+ <a class="orderly-shop-footer__link" href="/information/privatlivspolitik/">Privatlivspolitik</a>
69
+ </li>
70
+ <li class="orderly-shop-footer__link-item">
71
+ <a class="orderly-shop-footer__link" href="/information/kontakt/">Kontakt os</a>
72
+ </li>
73
+ </ul>
74
+ </section>
75
+ </div>
76
+ </template>
@@ -0,0 +1,32 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "Bundler",
7
+ "baseUrl": ".",
8
+ "paths": {
9
+ "@orderlyshop/core-client": [
10
+ "../../../core-client/src/index.ts"
11
+ ],
12
+ "@orderlyshop/core-client/*": [
13
+ "../../../core-client/src/*"
14
+ ],
15
+ "@orderlyshop/web-components": [
16
+ "../../src/index.ts"
17
+ ],
18
+ "@orderlyshop/web-components/*": [
19
+ "../../src/*.ts"
20
+ ]
21
+ },
22
+ "strict": true,
23
+ "noEmit": true,
24
+ "skipLibCheck": true,
25
+ "types": [
26
+ "vitest/globals"
27
+ ]
28
+ },
29
+ "include": [
30
+ "src/**/*.ts"
31
+ ]
32
+ }
@@ -0,0 +1,184 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
+ import { dirname, relative, resolve } from "node:path";
3
+ import { defineConfig } from "vite";
4
+
5
+ export default defineConfig({
6
+ server: {
7
+ host: "localhost",
8
+ port: 61677,
9
+ strictPort: true
10
+ },
11
+ preview: {
12
+ host: "localhost",
13
+ port: 61677,
14
+ strictPort: true
15
+ },
16
+ resolve: {
17
+ alias: sourceAliases()
18
+ },
19
+ plugins: [htmlIncludes(), sourceHtmlDevServer(), rootHtmlOutput()],
20
+ build: {
21
+ rollupOptions: {
22
+ input: htmlInputs()
23
+ }
24
+ }
25
+ });
26
+
27
+ function sourceAliases() {
28
+ const packageSource = resolve(process.cwd(), "../../src/index.ts");
29
+ if (!existsSync(packageSource)) {
30
+ return {};
31
+ }
32
+ return [{ find: "@orderlyshop/web-components", replacement: packageSource }];
33
+ }
34
+
35
+ function htmlIncludes() {
36
+ return {
37
+ name: "orderly-html-includes",
38
+ transformIndexHtml: {
39
+ order: "pre",
40
+ handler(html) {
41
+ return html
42
+ .replace("<!-- orderly-head-includes -->", include("head.html"))
43
+ .replace("<!-- orderly-body-start-includes -->", include("body-start.html"))
44
+ .replace("<!-- orderly-body-end-includes -->", include("body-end.html"));
45
+ }
46
+ }
47
+ };
48
+ }
49
+
50
+ function sourceHtmlDevServer() {
51
+ return {
52
+ name: "orderly-source-html-dev-server",
53
+ configureServer(server) {
54
+ server.middlewares.use(async (request, response, next) => {
55
+ const file = sourceHtmlFileForUrl(request.url ?? "/");
56
+ if (!file) {
57
+ next();
58
+ return;
59
+ }
60
+
61
+ try {
62
+ const html = await server.transformIndexHtml(request.url ?? "/", readFileSync(file, "utf8"));
63
+ response.statusCode = 200;
64
+ response.setHeader("Content-Type", "text/html");
65
+ response.end(html);
66
+ } catch (error) {
67
+ next(error);
68
+ }
69
+ });
70
+ }
71
+ };
72
+ }
73
+
74
+ function rootHtmlOutput() {
75
+ return {
76
+ name: "orderly-root-html-output",
77
+ enforce: "post",
78
+ generateBundle(_, bundle) {
79
+ for (const [fileName, output] of Object.entries(bundle)) {
80
+ if (!fileName.startsWith("src/") || !fileName.endsWith(".html")) {
81
+ continue;
82
+ }
83
+ delete bundle[fileName];
84
+ output.fileName = fileName.slice("src/".length);
85
+ bundle[output.fileName] = output;
86
+ }
87
+ }
88
+ };
89
+ }
90
+
91
+ function include(fileName) {
92
+ return includeFile(resolve(process.cwd(), "src/includes", fileName));
93
+ }
94
+
95
+ function includeFile(filePath) {
96
+ return expandTemplateIncludes(readFileSync(filePath, "utf8").trim(), dirname(filePath));
97
+ }
98
+
99
+ function expandTemplateIncludes(html, baseDir) {
100
+ return html.replace(/<!--\s*orderly-include-template:\s*([^>]+?)\s*-->/g, (_, templatePath) => {
101
+ return includeFile(resolve(baseDir, templatePath.trim()));
102
+ });
103
+ }
104
+
105
+ function htmlInputs(root = process.cwd()) {
106
+ const inputs = {};
107
+ for (const file of listHtmlFiles(root)) {
108
+ const name = htmlInputName(root, file);
109
+ inputs[name] = file;
110
+ }
111
+ return inputs;
112
+ }
113
+
114
+ function htmlInputName(root, file) {
115
+ const name = relative(root, file).replace(/\\/g, "/").replace(/\.html$/, "");
116
+ return name.startsWith("src/") ? name.slice("src/".length) : name;
117
+ }
118
+
119
+ function listHtmlFiles(dir) {
120
+ const files = [];
121
+ for (const entry of readdirSync(dir)) {
122
+ if (entry === "dist" || entry === "node_modules") {
123
+ continue;
124
+ }
125
+ const fullPath = resolve(dir, entry);
126
+ if (isHtmlPartial(fullPath)) {
127
+ continue;
128
+ }
129
+ const stat = statSync(fullPath);
130
+ if (stat.isDirectory()) {
131
+ files.push(...listHtmlFiles(fullPath));
132
+ } else if (entry.endsWith(".html")) {
133
+ files.push(fullPath);
134
+ }
135
+ }
136
+ return files;
137
+ }
138
+
139
+ function isHtmlPartial(file) {
140
+ const path = relative(process.cwd(), file);
141
+ return path.startsWith("src/includes") || path.startsWith("src/templates");
142
+ }
143
+
144
+ function sourceHtmlFileForUrl(url) {
145
+ const requestPath = safeHtmlRequestPath(url);
146
+ if (!requestPath) {
147
+ return undefined;
148
+ }
149
+ const categoryTemplate = resolve(process.cwd(), "src/category.html");
150
+ if (requestPath.startsWith("categories/") && requestPath.endsWith("index.html") && existsSync(categoryTemplate)) {
151
+ return categoryTemplate;
152
+ }
153
+ const file = resolve(process.cwd(), "src", requestPath);
154
+ if (!file.startsWith(resolve(process.cwd(), "src")) || isHtmlPartial(file) || !existsSync(file)) {
155
+ return undefined;
156
+ }
157
+ return statSync(file).isFile() ? file : undefined;
158
+ }
159
+
160
+ function safeHtmlRequestPath(url) {
161
+ const [rawPath] = url.split(/[?#]/, 1);
162
+ let path;
163
+ try {
164
+ path = decodeURIComponent(rawPath || "/");
165
+ } catch {
166
+ return undefined;
167
+ }
168
+ if (path.includes("..")) {
169
+ return undefined;
170
+ }
171
+ if (path === "/") {
172
+ return "index.html";
173
+ }
174
+ if (path.endsWith("/")) {
175
+ return `${path.slice(1)}index.html`;
176
+ }
177
+ if (path.startsWith("/categories/") && !path.includes(".")) {
178
+ return `${path.slice(1)}/index.html`;
179
+ }
180
+ if (path.endsWith(".html")) {
181
+ return path.slice(1);
182
+ }
183
+ return undefined;
184
+ }