@knitli/astro-docs-template 0.1.1 → 0.1.2

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/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bun
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
@@ -1,43 +1,25 @@
1
1
  #!/usr/bin/env bun
2
- // SPDX-FileCopyrightText: 2026 Knitli Inc.
3
- //
4
- // SPDX-License-Identifier: Apache-2.0
5
-
6
- import { createInterface } from "node:readline/promises";
7
- import { stdin, stdout } from "node:process";
8
- import {
9
- addPieces,
10
- initDocsTemplate,
11
- PIECES,
12
- PIECE_NAMES,
13
- type InitOptions,
14
- type PieceName,
15
- } from "./index.js";
16
-
17
- // ── Arg parsing ──
18
-
19
- function parseArgs(argv: string[]) {
20
- const flags: Record<string, string> = {};
21
- const positional: string[] = [];
2
+ import { stdout, stdin } from 'node:process';
3
+ import { createInterface } from 'node:readline/promises';
4
+ import { PIECES, PIECE_NAMES, addPieces, initDocsTemplate } from './index.js';
22
5
 
6
+ function parseArgs(argv) {
7
+ const flags = {};
8
+ const positional = [];
23
9
  for (let i = 0; i < argv.length; i++) {
24
- const arg = argv[i]!;
10
+ const arg = argv[i];
25
11
  if (arg === "--help" || arg === "-h") {
26
12
  flags.help = "true";
27
13
  } else if (arg === "--force" || arg === "-f") {
28
14
  flags.force = "true";
29
15
  } else if (arg.startsWith("--") && i + 1 < argv.length) {
30
- flags[arg.slice(2)] = argv[++i]!;
16
+ flags[arg.slice(2)] = argv[++i];
31
17
  } else {
32
18
  positional.push(arg);
33
19
  }
34
20
  }
35
-
36
21
  return { flags, positional };
37
22
  }
38
-
39
- // ── Help text ──
40
-
41
23
  const HELP = `knitli-docs — scaffold and manage Knitli docs sites
42
24
 
43
25
  Usage:
@@ -46,9 +28,7 @@ Usage:
46
28
  knitli-docs list Show available pieces
47
29
 
48
30
  Pieces:
49
- ${Object.entries(PIECES)
50
- .map(([name, { description }]) => ` ${name.padEnd(18)} ${description}`)
51
- .join("\n")}
31
+ ${Object.entries(PIECES).map(([name, { description }]) => ` ${name.padEnd(18)} ${description}`).join("\n")}
52
32
 
53
33
  Options:
54
34
  --app-name <name> Product name (e.g. "Recoco")
@@ -59,60 +39,37 @@ Options:
59
39
  --force, -f Overwrite existing files
60
40
  --help, -h Show this help
61
41
  `;
62
-
63
- // ── Interactive prompts ──
64
-
65
- async function getOptions(
66
- flags: Record<string, string>,
67
- ): Promise<InitOptions> {
42
+ async function getOptions(flags) {
68
43
  const rl = createInterface({ input: stdin, output: stdout });
69
-
70
- const ask = async (
71
- question: string,
72
- defaultValue?: string,
73
- ): Promise<string> => {
44
+ const ask = async (question, defaultValue) => {
74
45
  const suffix = defaultValue ? ` [${defaultValue}]` : "";
75
46
  const answer = await rl.question(`${question}${suffix}: `);
76
47
  return answer.trim() || defaultValue || "";
77
48
  };
78
-
79
49
  try {
80
- const appName =
81
- flags["app-name"] || (await ask("Product name (e.g. Recoco)"));
50
+ const appName = flags["app-name"] || await ask("Product name (e.g. Recoco)");
82
51
  if (!appName) {
83
52
  console.error("Error: product name is required");
84
53
  process.exit(1);
85
54
  }
86
-
87
- const name =
88
- flags.name ||
89
- (await ask(
90
- "npm package name",
91
- `@knitli-site/${appName.toLowerCase()}-docs`,
92
- ));
93
- const description =
94
- flags.description || (await ask("Short description"));
95
- const workerName =
96
- flags["worker-name"] ||
97
- (await ask("Worker name", `${appName.toLowerCase()}-docs`));
98
-
55
+ const name = flags.name || await ask(
56
+ "npm package name",
57
+ `@knitli-site/${appName.toLowerCase()}-docs`
58
+ );
59
+ const description = flags.description || await ask("Short description");
60
+ const workerName = flags["worker-name"] || await ask("Worker name", `${appName.toLowerCase()}-docs`);
99
61
  return { appName, name, description, workerName };
100
62
  } finally {
101
63
  rl.close();
102
64
  }
103
65
  }
104
-
105
- // ── Commands ──
106
-
107
66
  async function main() {
108
67
  const { flags, positional } = parseArgs(process.argv.slice(2));
109
68
  const command = positional[0];
110
-
111
69
  if (flags.help || !command) {
112
70
  console.log(HELP);
113
71
  process.exit(0);
114
72
  }
115
-
116
73
  switch (command) {
117
74
  case "list": {
118
75
  console.log("\nAvailable pieces:\n");
@@ -125,54 +82,55 @@ async function main() {
125
82
  console.log();
126
83
  break;
127
84
  }
128
-
129
85
  case "init": {
130
86
  const dir = flags.dir || positional[1] || ".";
131
87
  const options = await getOptions(flags);
132
- console.log(`\nScaffolding into ${dir} ...\n`);
88
+ console.log(`
89
+ Scaffolding into ${dir} ...
90
+ `);
133
91
  const created = initDocsTemplate(dir, options);
134
- console.log(`\nDone — ${created.length} files created`);
92
+ console.log(`
93
+ Done — ${created.length} files created`);
135
94
  break;
136
95
  }
137
-
138
96
  case "add": {
139
97
  const pieces = positional.slice(1).filter((p) => !p.startsWith("-"));
140
98
  if (pieces.length === 0) {
141
99
  console.error(
142
- "Error: specify at least one piece. Run 'knitli-docs list' to see options.",
100
+ "Error: specify at least one piece. Run 'knitli-docs list' to see options."
143
101
  );
144
102
  process.exit(1);
145
103
  }
146
-
147
104
  const invalid = pieces.filter(
148
- (p) => !PIECE_NAMES.includes(p as PieceName),
105
+ (p) => !PIECE_NAMES.includes(p)
149
106
  );
150
107
  if (invalid.length > 0) {
151
108
  console.error(`Error: unknown piece(s): ${invalid.join(", ")}`);
152
109
  console.error(`Available: ${PIECE_NAMES.join(", ")}`);
153
110
  process.exit(1);
154
111
  }
155
-
156
112
  const dir = flags.dir || ".";
157
113
  const options = await getOptions(flags);
158
- console.log(`\nAdding ${pieces.join(", ")} to ${dir} ...\n`);
114
+ console.log(`
115
+ Adding ${pieces.join(", ")} to ${dir} ...
116
+ `);
159
117
  const created = addPieces(dir, {
160
118
  ...options,
161
- pieces: pieces as PieceName[],
162
- force: flags.force === "true",
119
+ pieces,
120
+ force: flags.force === "true"
163
121
  });
164
- console.log(`\nDone — ${created.length} files created`);
122
+ console.log(`
123
+ Done — ${created.length} files created`);
165
124
  break;
166
125
  }
167
-
168
126
  default: {
169
- console.error(`Unknown command: ${command}\n`);
127
+ console.error(`Unknown command: ${command}
128
+ `);
170
129
  console.log(HELP);
171
130
  process.exit(1);
172
131
  }
173
132
  }
174
133
  }
175
-
176
134
  main().catch((err) => {
177
135
  console.error(err.message);
178
136
  process.exit(1);
@@ -0,0 +1,59 @@
1
+ import type { OutgoingHttpHeaders } from "node:http2";
2
+ type defaultIntegration = "sitemap" | "astroD2" | "markdoc" | "mdx" | "favicons" | "cloudflare-pages-headers";
3
+ export declare const headlineLogoDark: string, headlineLogoLight: string, variables: string, docsStyle: string, faviconIco: string, faviconSvg: string;
4
+ declare const shikiCfg: {
5
+ themes: {
6
+ light: "catppuccin-latte";
7
+ dark: "catppuccin-mocha";
8
+ };
9
+ bundledLangs: string[];
10
+ langAlias: {
11
+ js: string;
12
+ md: string;
13
+ py: string;
14
+ rs: string;
15
+ sh: string;
16
+ yml: string;
17
+ };
18
+ };
19
+ declare const imgDomains: {
20
+ protocol: string;
21
+ hostname: string;
22
+ }[];
23
+ export interface DocsTemplateOptions {
24
+ appName: string;
25
+ description: string;
26
+ llmConfig: {
27
+ llmDescription: string;
28
+ promotePatterns: string[];
29
+ demotePatterns: string[];
30
+ };
31
+ rootDir: string;
32
+ shikiConfig?: typeof shikiCfg;
33
+ logoDark?: string;
34
+ logoLight?: string;
35
+ imageDomains?: typeof imgDomains;
36
+ is_codeweaver?: boolean;
37
+ sitemapFilter?: (page: string) => boolean;
38
+ linkValidationConfig?: Record<string, unknown>;
39
+ sidebarConfig?: {
40
+ label: string;
41
+ autogenerate: {
42
+ directory: string;
43
+ };
44
+ }[];
45
+ unwantedPlugins?: string[];
46
+ pluginConfigs?: Record<string, any>;
47
+ unwantedIntegrations?: defaultIntegration[];
48
+ integrationConfigs?: Record<string, any>;
49
+ headersConfig?: OutgoingHttpHeaders;
50
+ }
51
+ export type IntegrationOptions = Pick<DocsTemplateOptions, "appName" | "sitemapFilter" | "integrationConfigs"> & {
52
+ unwantedIntegrations?: defaultIntegration[];
53
+ };
54
+ export type PluginOptions = Pick<DocsTemplateOptions, "appName" | "llmConfig" | "pluginConfigs"> & {
55
+ unwantedPlugins?: string[];
56
+ };
57
+ export default function createConfig(options: DocsTemplateOptions): import("astro").AstroUserConfig<never, never, never>;
58
+ export {};
59
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AA6BtD,KAAK,kBAAkB,GACnB,SAAS,GACT,SAAS,GACT,SAAS,GACT,KAAK,GACL,UAAU,GACV,0BAA0B,CAAC;AAQ/B,eAAO,MACL,gBAAgB,UAChB,iBAAiB,UACjB,SAAS,UACT,SAAS,UACT,UAAU,UACV,UAAU,QACE,CAAC;AAEf,QAAA,MAAM,QAAQ;;;;;;;;;;;;;;CAyBb,CAAC;AAEF,QAAA,MAAM,UAAU;;;GAMf,CAAC;AA8DF,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE;QACT,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,cAAc,EAAE,MAAM,EAAE,CAAC;KAC1B,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,QAAQ,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,UAAU,CAAC;IACjC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC1C,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/C,aAAa,CAAC,EAAE;QACd,KAAK,EAAE,MAAM,CAAC;QACd,YAAY,EAAE;YAAE,SAAS,EAAE,MAAM,CAAA;SAAE,CAAC;KACrC,EAAE,CAAC;IACJ,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAE3B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACpC,oBAAoB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAE5C,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACzC,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,mBAAmB,EACnB,SAAS,GAAG,eAAe,GAAG,oBAAoB,CACnD,GAAG;IAAE,oBAAoB,CAAC,EAAE,kBAAkB,EAAE,CAAA;CAAE,CAAC;AA+DpD,MAAM,MAAM,aAAa,GAAG,IAAI,CAC9B,mBAAmB,EACnB,SAAS,GAAG,WAAW,GAAG,eAAe,CAC1C,GAAG;IAAE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAoEnC,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,OAAO,EAAE,mBAAmB,wDAuOhE"}
@@ -1,65 +1,43 @@
1
- // SPDX-FileCopyrightText: 2026 Knitli Inc.
2
- //
3
- // SPDX-License-Identifier: Apache-2.0
1
+ import cloudflare from '@astrojs/cloudflare';
2
+ import markdoc from '@astrojs/markdoc';
3
+ import mdx from '@astrojs/mdx';
4
+ import sitemap from '@astrojs/sitemap';
5
+ import starlight from '@astrojs/starlight';
6
+ import { DocsAssets } from '@knitli/docs-components';
7
+ import llmEnhancements from '@nuasite/llm-enhancements';
8
+ import { fontProviders, defineConfig } from 'astro/config';
9
+ import cloudflarePagesHeaders from 'astro-cloudflare-pages-headers';
10
+ import astroD2 from 'astro-d2';
11
+ import favicons from 'astro-favicons';
12
+ import rehypeExternalLinks from 'rehype-external-links';
13
+ import starlightAnnouncement from 'starlight-announcement';
14
+ import starlightChangelogs from 'starlight-changelogs';
15
+ import starlightHeadingBadges from 'starlight-heading-badges';
16
+ import starlightLinksValidator from 'starlight-links-validator';
17
+ import starlightLlmsText from 'starlight-llms-txt';
18
+ import starlightPageActions from 'starlight-page-actions';
19
+ import { starlightIconsIntegration, starlightIconsPlugin } from 'starlight-plugin-icons';
20
+ import starlightScrollToTop from 'starlight-scroll-to-top';
21
+ import starlightSidebarTopics from 'starlight-sidebar-topics';
22
+ import starlightTags from 'starlight-tags';
23
+ import { searchForWorkspaceRoot } from 'vite';
24
+ import viteTsconfigPaths from 'vite-tsconfig-paths';
4
25
 
5
- // @ts-check
6
-
7
- import type { OutgoingHttpHeaders } from "node:http2";
8
- import cloudflare from "@astrojs/cloudflare";
9
- import markdoc from "@astrojs/markdoc";
10
- import mdx from "@astrojs/mdx";
11
- import sitemap from "@astrojs/sitemap";
12
- import starlight from "@astrojs/starlight";
13
- import { DocsAssets } from "@knitli/docs-components";
14
- import llmEnhancements from "@nuasite/llm-enhancements";
15
- import { defineConfig, fontProviders } from "astro/config";
16
- import cloudflarePagesHeaders from "astro-cloudflare-pages-headers";
17
- import astroD2 from "astro-d2";
18
- import favicons from "astro-favicons";
19
- import rehypeExternalLinks from "rehype-external-links";
20
- import starlightAnnouncement from "starlight-announcement";
21
- import starlightChangelogs from "starlight-changelogs";
22
- import starlightHeadingBadges from "starlight-heading-badges";
23
- import starlightLinksValidator from "starlight-links-validator";
24
- import starlightLlmsText from "starlight-llms-txt";
25
- import starlightPageActions from "starlight-page-actions";
26
- import {
27
- starlightIconsIntegration,
28
- starlightIconsPlugin,
29
- } from "starlight-plugin-icons";
30
- import starlightScrollToTop from "starlight-scroll-to-top";
31
- import starlightSidebarTopics from "starlight-sidebar-topics";
32
- import starlightTags from "starlight-tags";
33
- import { searchForWorkspaceRoot } from "vite";
34
- import viteTsconfigPaths from "vite-tsconfig-paths";
35
-
36
- type defaultIntegration =
37
- | "sitemap"
38
- | "astroD2"
39
- | "markdoc"
40
- | "mdx"
41
- | "favicons"
42
- | "cloudflare-pages-headers";
43
-
44
- function nonNullable<T>(value: T): value is NonNullable<T> {
26
+ function nonNullable(value) {
45
27
  return value != null;
46
28
  }
47
-
48
- // ── Defaults (defined before interface so `typeof` references work) ──
49
-
50
- export const {
29
+ const {
51
30
  headlineLogoDark,
52
31
  headlineLogoLight,
53
32
  variables,
54
33
  docsStyle,
55
34
  faviconIco,
56
- faviconSvg,
35
+ faviconSvg
57
36
  } = DocsAssets;
58
-
59
37
  const shikiCfg = {
60
38
  themes: {
61
- light: "catppuccin-latte" as const,
62
- dark: "catppuccin-mocha" as const,
39
+ light: "catppuccin-latte",
40
+ dark: "catppuccin-mocha"
63
41
  },
64
42
  bundledLangs: [
65
43
  "ansi",
@@ -71,7 +49,7 @@ const shikiCfg = {
71
49
  "rust",
72
50
  "toml",
73
51
  "typescript",
74
- "yaml",
52
+ "yaml"
75
53
  ],
76
54
  langAlias: {
77
55
  js: "typescript",
@@ -79,64 +57,61 @@ const shikiCfg = {
79
57
  py: "python",
80
58
  rs: "rust",
81
59
  sh: "bash",
82
- yml: "yaml",
83
- },
60
+ yml: "yaml"
61
+ }
84
62
  };
85
-
86
63
  const imgDomains = [
87
64
  { protocol: "https", hostname: "ui-avatars.com" },
88
65
  { protocol: "https", hostname: "knitli.com" },
89
66
  { protocol: "https", hostname: "*.knitli.com" },
90
67
  { protocol: "https", hostname: "*.githubusercontent.com" },
91
- { protocol: "https", hostname: "*.cloudflare.com" },
68
+ { protocol: "https", hostname: "*.cloudflare.com" }
92
69
  ];
93
-
94
70
  const defaultFontConfig = [
95
71
  {
96
72
  provider: fontProviders.google(),
97
73
  name: "DM Mono",
98
74
  cssVariable: "--font-sans",
99
- weights: [400, 500, 700] as [number, ...number[]],
100
- styles: ["normal", "italic"] as [string, ...string[]],
101
- subsets: ["latin"] as [string, ...string[]],
102
- formats: ["woff2"] as [string, ...string[]],
75
+ weights: [400, 500, 700],
76
+ styles: ["normal", "italic"],
77
+ subsets: ["latin"],
78
+ formats: ["woff2"],
103
79
  fallbacks: [
104
80
  "Roboto Mono",
105
81
  "Menlo",
106
82
  "Consolas",
107
83
  "DejaVu Sans Mono",
108
- "monospace",
109
- ],
84
+ "monospace"
85
+ ]
110
86
  },
111
87
  {
112
88
  provider: fontProviders.google(),
113
89
  name: "JetBrains Mono",
114
90
  cssVariable: "--font-mono",
115
- weights: [400, 500, 700] as [number, ...number[]],
116
- styles: ["normal", "italic"] as [string, ...string[]],
117
- subsets: ["latin"] as [string, ...string[]],
118
- formats: ["woff2"] as [string, ...string[]],
91
+ weights: [400, 500, 700],
92
+ styles: ["normal", "italic"],
93
+ subsets: ["latin"],
94
+ formats: ["woff2"],
119
95
  fallbacks: [
120
96
  "DM Mono",
121
97
  "Roboto Mono",
122
98
  "Menlo",
123
99
  "Consolas",
124
100
  "DejaVu Sans Mono",
125
- "monospace",
126
- ],
127
- },
101
+ "monospace"
102
+ ]
103
+ }
128
104
  ];
129
-
130
105
  const codeweaverFontConfig = [
131
106
  { ...defaultFontConfig[1], cssVariable: "--font-sans" },
132
107
  {
133
108
  provider: fontProviders.google(),
134
109
  name: "IBM Plex Mono",
135
110
  cssVariable: "--font-mono",
136
- weights: [400, 500, 700] as [number, ...number[]],
137
- styles: ["normal", "italic"] as [string, ...string[]],
138
- subsets: ["latin"] as [string, ...string[]],
139
- formats: ["woff2"] as [string, ...string[]],
111
+ weights: [400, 500, 700],
112
+ styles: ["normal", "italic"],
113
+ subsets: ["latin"],
114
+ formats: ["woff2"],
140
115
  fallbacks: [
141
116
  "JetBrains Mono",
142
117
  "DM Mono",
@@ -144,53 +119,16 @@ const codeweaverFontConfig = [
144
119
  "Menlo",
145
120
  "Consolas",
146
121
  "DejaVu Sans Mono",
147
- "monospace",
148
- ],
149
- },
122
+ "monospace"
123
+ ]
124
+ }
150
125
  ];
151
-
152
- // ── Public interface ──
153
-
154
- export interface DocsTemplateOptions {
155
- appName: string;
156
- description: string;
157
- llmConfig: {
158
- llmDescription: string;
159
- promotePatterns: string[];
160
- demotePatterns: string[];
161
- };
162
- rootDir: string;
163
- shikiConfig?: typeof shikiCfg;
164
- logoDark?: string;
165
- logoLight?: string;
166
- imageDomains?: typeof imgDomains;
167
- is_codeweaver?: boolean;
168
- sitemapFilter?: (page: string) => boolean;
169
- linkValidationConfig?: Record<string, unknown>;
170
- sidebarConfig?: {
171
- label: string;
172
- autogenerate: { directory: string };
173
- }[];
174
- unwantedPlugins?: string[];
175
- // biome-ignore lint/suspicious/noExplicitAny: plugin configs are opaque pass-throughs
176
- pluginConfigs?: Record<string, any>;
177
- unwantedIntegrations?: defaultIntegration[];
178
- // biome-ignore lint/suspicious/noExplicitAny: integration configs are opaque pass-throughs
179
- integrationConfigs?: Record<string, any>;
180
- headersConfig?: OutgoingHttpHeaders;
181
- }
182
-
183
- export type IntegrationOptions = Pick<
184
- DocsTemplateOptions,
185
- "appName" | "sitemapFilter" | "integrationConfigs"
186
- > & { unwantedIntegrations?: defaultIntegration[] };
187
-
188
- const getIntegrations = (options: IntegrationOptions) => {
126
+ const getIntegrations = (options) => {
189
127
  const {
190
128
  appName,
191
129
  sitemapFilter,
192
130
  integrationConfigs,
193
- unwantedIntegrations = [],
131
+ unwantedIntegrations = []
194
132
  } = options;
195
133
  const defaultIntegrations = [
196
134
  astroD2({ skipGeneration: true }),
@@ -201,57 +139,45 @@ const getIntegrations = (options: IntegrationOptions) => {
201
139
  name: `${appName} Docs by Knitli`,
202
140
  short_name: `${appName} Docs`,
203
141
  input: {
204
- favicons: [faviconSvg],
205
- },
142
+ favicons: [faviconSvg]
143
+ }
206
144
  }),
207
145
  cloudflarePagesHeaders({}),
208
146
  sitemap({
209
147
  filter: sitemapFilter || ((page) => !/\^\/(?!cdn-cgi\/)/.test(page)),
210
148
  changefreq: "weekly",
211
149
  priority: 0.4,
212
- lastmod: new Date(),
150
+ lastmod: /* @__PURE__ */ new Date(),
213
151
  namespaces: {
214
152
  image: false,
215
- video: false,
216
- },
217
- }),
153
+ video: false
154
+ }
155
+ })
218
156
  ];
219
-
220
157
  const filtered = defaultIntegrations.filter((integration) => {
221
158
  const name = integration?.name;
222
- return !name || !unwantedIntegrations.includes(name as defaultIntegration);
159
+ return !name || !unwantedIntegrations.includes(name);
223
160
  });
224
-
225
161
  if (!integrationConfigs) return filtered;
226
-
227
- const overrides = Object.entries(integrationConfigs)
228
- .map(([integrationName, config]) => {
229
- switch (integrationName) {
230
- case "astroD2":
231
- return astroD2(config);
232
- case "markdoc":
233
- return markdoc(config);
234
- case "mdx":
235
- return mdx(config);
236
- case "favicons":
237
- return favicons(config);
238
- case "sitemap":
239
- return sitemap(config);
240
- default:
241
- return null;
242
- }
243
- })
244
- .filter(nonNullable);
245
-
162
+ const overrides = Object.entries(integrationConfigs).map(([integrationName, config]) => {
163
+ switch (integrationName) {
164
+ case "astroD2":
165
+ return astroD2(config);
166
+ case "markdoc":
167
+ return markdoc(config);
168
+ case "mdx":
169
+ return mdx(config);
170
+ case "favicons":
171
+ return favicons(config);
172
+ case "sitemap":
173
+ return sitemap(config);
174
+ default:
175
+ return null;
176
+ }
177
+ }).filter(nonNullable);
246
178
  return [...filtered, ...overrides];
247
179
  };
248
-
249
- export type PluginOptions = Pick<
250
- DocsTemplateOptions,
251
- "appName" | "llmConfig" | "pluginConfigs"
252
- > & { unwantedPlugins?: string[] };
253
-
254
- const get_plugins = (options: PluginOptions) => {
180
+ const get_plugins = (options) => {
255
181
  const { appName, llmConfig, pluginConfigs, unwantedPlugins = [] } = options;
256
182
  const defaultPlugins = [
257
183
  starlightAnnouncement(),
@@ -263,12 +189,10 @@ const get_plugins = (options: PluginOptions) => {
263
189
  starlightPageActions({
264
190
  baseUrl: `https://docs.knitli.com/${appName.toLowerCase()}`,
265
191
  actions: { claude: true, chatgpt: true, markdown: true },
266
- share: true,
192
+ share: true
267
193
  }),
268
194
  starlightTags({ onInlineTagsNotFound: "warn" }),
269
- pluginConfigs?.starlightSidebarTopics
270
- ? starlightSidebarTopics(pluginConfigs.starlightSidebarTopics)
271
- : null,
195
+ pluginConfigs?.starlightSidebarTopics ? starlightSidebarTopics(pluginConfigs.starlightSidebarTopics) : null,
272
196
  starlightScrollToTop({ showOnHomepage: false }),
273
197
  // We need to configure starlight-tags with a tags.yml.
274
198
  //starlightTags(),
@@ -280,44 +204,35 @@ const get_plugins = (options: PluginOptions) => {
280
204
  minify: {
281
205
  whitespace: true,
282
206
  note: true,
283
- details: true,
284
- },
285
- }),
286
- ];
287
-
288
- const filtered = defaultPlugins
289
- .filter(nonNullable)
290
- .filter((plugin) => !unwantedPlugins.includes(plugin.name));
291
-
292
- if (!pluginConfigs) return filtered;
293
-
294
- const overrides = Object.entries(pluginConfigs)
295
- .map(([pluginName, config]) => {
296
- switch (pluginName) {
297
- case "starlightAnnouncement":
298
- return starlightAnnouncement(config);
299
- case "starlightIconsIntegration":
300
- return starlightIconsIntegration(config);
301
- case "starlightIconsPlugin":
302
- return starlightIconsPlugin(config);
303
- case "starlightLinksValidator":
304
- return starlightLinksValidator(config);
305
- case "starlightPageActions":
306
- return starlightPageActions(config);
307
- case "starlightTags":
308
- return starlightTags(config);
309
- case "starlightScrollToTop":
310
- return starlightScrollToTop(config);
311
- default:
312
- return null;
207
+ details: true
313
208
  }
314
209
  })
315
- .filter(nonNullable);
316
-
210
+ ];
211
+ const filtered = defaultPlugins.filter(nonNullable).filter((plugin) => !unwantedPlugins.includes(plugin.name));
212
+ if (!pluginConfigs) return filtered;
213
+ const overrides = Object.entries(pluginConfigs).map(([pluginName, config]) => {
214
+ switch (pluginName) {
215
+ case "starlightAnnouncement":
216
+ return starlightAnnouncement(config);
217
+ case "starlightIconsIntegration":
218
+ return starlightIconsIntegration(config);
219
+ case "starlightIconsPlugin":
220
+ return starlightIconsPlugin(config);
221
+ case "starlightLinksValidator":
222
+ return starlightLinksValidator(config);
223
+ case "starlightPageActions":
224
+ return starlightPageActions(config);
225
+ case "starlightTags":
226
+ return starlightTags(config);
227
+ case "starlightScrollToTop":
228
+ return starlightScrollToTop(config);
229
+ default:
230
+ return null;
231
+ }
232
+ }).filter(nonNullable);
317
233
  return [...filtered, ...overrides];
318
234
  };
319
-
320
- export default function createConfig(options: DocsTemplateOptions) {
235
+ function createConfig(options) {
321
236
  const {
322
237
  appName,
323
238
  description,
@@ -334,59 +249,57 @@ export default function createConfig(options: DocsTemplateOptions) {
334
249
  integrationConfigs,
335
250
  unwantedIntegrations,
336
251
  unwantedPlugins,
337
- headersConfig,
252
+ headersConfig
338
253
  } = options;
339
-
340
- // https://astro.build/config
341
254
  return defineConfig({
342
255
  site: "https://docs.knitli.com",
343
256
  base: `/${appName.toLowerCase()}/`,
344
257
  adapter: cloudflare({
345
258
  prerenderEnvironment: "workerd",
346
259
  experimental: {
347
- headersAndRedirectsDevModeSupport: true,
260
+ headersAndRedirectsDevModeSupport: true
348
261
  },
349
262
  configPath: `${rootDir}/wrangler.jsonc`,
350
- imageService: "compile",
263
+ imageService: "compile"
351
264
  }),
352
265
  // Image optimization
353
266
  image: {
354
267
  service: {
355
- entrypoint: "astro/assets/services/sharp",
268
+ entrypoint: "astro/assets/services/sharp"
356
269
  },
357
270
  responsiveStyles: true,
358
271
  layout: "constrained",
359
- remotePatterns: imageDomains,
272
+ remotePatterns: imageDomains
360
273
  },
361
274
  compressHTML: true,
362
275
  // biome-ignore lint/suspicious/noExplicitAny: Astro's FontFamily generic inference doesn't match spread patterns
363
- fonts: (is_codeweaver ? codeweaverFontConfig : defaultFontConfig) as any,
276
+ fonts: is_codeweaver ? codeweaverFontConfig : defaultFontConfig,
364
277
  // Build optimizations
365
278
  build: {
366
279
  inlineStylesheets: "auto",
367
- assets: "_astro",
280
+ assets: "_astro"
368
281
  },
369
282
  markdown: {
370
283
  shikiConfig: Object.fromEntries(
371
- Object.entries(shikiConfig).filter(([key]) => key !== "bundledLangs"),
284
+ Object.entries(shikiConfig).filter(([key]) => key !== "bundledLangs")
372
285
  ),
373
286
  rehypePlugins: [
374
287
  rehypeExternalLinks({
375
288
  content: { type: "text", value: " ↗" },
376
- rel: ["nofollow"],
377
- }),
378
- ],
289
+ rel: ["nofollow"]
290
+ })
291
+ ]
379
292
  },
380
293
  server: {
381
- headers: headersConfig,
294
+ headers: headersConfig
382
295
  },
383
296
  trailingSlash: "always",
384
297
  // Vite configuration for better bundling
385
298
  vite: {
386
299
  server: {
387
300
  fs: {
388
- allow: [searchForWorkspaceRoot(rootDir)],
389
- },
301
+ allow: [searchForWorkspaceRoot(rootDir)]
302
+ }
390
303
  },
391
304
  assetsInclude: [
392
305
  "src/*.webp",
@@ -394,11 +307,11 @@ export default function createConfig(options: DocsTemplateOptions) {
394
307
  "src/*.jpg",
395
308
  "src/*.jpeg",
396
309
  "src/*.svg",
397
- "src/*.avif",
310
+ "src/*.avif"
398
311
  ],
399
312
  plugins: [viteTsconfigPaths({ loose: true })],
400
313
  define: {
401
- "import.meta.env.PUBLIC_DOCS_PRODUCT": JSON.stringify(appName),
314
+ "import.meta.env.PUBLIC_DOCS_PRODUCT": JSON.stringify(appName)
402
315
  },
403
316
  build: {
404
317
  cssMinify: "lightningcss",
@@ -411,7 +324,7 @@ export default function createConfig(options: DocsTemplateOptions) {
411
324
  dir: `${rootDir}/dist/_astro/`,
412
325
  compact: true,
413
326
  interop: "esModule",
414
- experimentalMinChunkSize: 10000,
327
+ experimentalMinChunkSize: 1e4,
415
328
  banner: `
416
329
  /* SPDX-FileCopyrightText: 2026 Knitli Inc.
417
330
  * SPDX-License-Identifier: MIT OR Apache-2.0
@@ -423,22 +336,22 @@ export default function createConfig(options: DocsTemplateOptions) {
423
336
  arrowFunctions: true,
424
337
  constBindings: true,
425
338
  objectShorthand: true,
426
- symbols: true,
339
+ symbols: true
427
340
  },
428
341
  entryFileNames: "assets/[name]-[hash].js",
429
342
  chunkFileNames: "assets/[name]-[hash].js",
430
- assetFileNames: "assets/[name]-[hash][extname]",
343
+ assetFileNames: "assets/[name]-[hash][extname]"
431
344
  },
432
- treeshake: "smallest",
345
+ treeshake: "smallest"
433
346
  },
434
- ssr: false,
347
+ ssr: false
435
348
  },
436
349
  css: {
437
- lightningcss: {},
438
- },
350
+ lightningcss: {}
351
+ }
439
352
  },
440
353
  prefetch: {
441
- defaultStrategy: "viewport",
354
+ defaultStrategy: "viewport"
442
355
  },
443
356
  prerenderConflictBehavior: "warn",
444
357
  experimental: {
@@ -447,7 +360,7 @@ export default function createConfig(options: DocsTemplateOptions) {
447
360
  contentIntellisense: true,
448
361
  queuedRendering: {
449
362
  contentCache: true,
450
- enabled: true,
363
+ enabled: true
451
364
  },
452
365
  rustCompiler: true,
453
366
  svgo: {
@@ -456,14 +369,13 @@ export default function createConfig(options: DocsTemplateOptions) {
456
369
  name: "preset-default",
457
370
  params: {
458
371
  overrides: {
459
- removeMetadata: false,
460
- },
461
- },
462
- },
463
- ],
464
- },
372
+ removeMetadata: false
373
+ }
374
+ }
375
+ }
376
+ ]
377
+ }
465
378
  },
466
-
467
379
  // Static site generation for Cloudflare
468
380
  output: "static",
469
381
  integrations: [
@@ -471,20 +383,20 @@ export default function createConfig(options: DocsTemplateOptions) {
471
383
  appName,
472
384
  sitemapFilter,
473
385
  integrationConfigs,
474
- unwantedIntegrations,
386
+ unwantedIntegrations
475
387
  }),
476
388
  starlight({
477
389
  title: `${appName} Docs`,
478
390
  pagefind: true,
479
- description: description,
391
+ description,
480
392
  logo: {
481
393
  dark: logoDark,
482
394
  light: logoLight,
483
395
  alt: is_codeweaver ? "CodeWeaver Logo" : "Knitli Logo",
484
- replacesTitle: true,
396
+ replacesTitle: true
485
397
  },
486
398
  editLink: {
487
- baseUrl: `https://github.com/knitli/${appName.toLowerCase()}/edit/main/docs-site/src`,
399
+ baseUrl: `https://github.com/knitli/${appName.toLowerCase()}/edit/main/docs-site/src`
488
400
  },
489
401
  expressiveCode: {
490
402
  useStarlightDarkModeSwitch: true,
@@ -492,27 +404,27 @@ export default function createConfig(options: DocsTemplateOptions) {
492
404
  removeUnusedThemes: true,
493
405
  shiki: {
494
406
  ...Object.fromEntries(
495
- Object.entries(shikiConfig).filter(([key]) => key !== "themes"),
496
- ),
497
- },
407
+ Object.entries(shikiConfig).filter(([key]) => key !== "themes")
408
+ )
409
+ }
498
410
  },
499
411
  plugins: get_plugins({
500
412
  appName,
501
413
  llmConfig,
502
414
  pluginConfigs,
503
- unwantedPlugins,
415
+ unwantedPlugins
504
416
  // biome-ignore lint/suspicious/noExplicitAny: Starlight plugin types are complex Zod inferences
505
- }) as any[],
417
+ }),
506
418
  social: [
507
419
  {
508
420
  icon: "github",
509
421
  label: "GitHub",
510
- href: `https://github.com/knitli/${appName.toLowerCase()}`,
511
- },
422
+ href: `https://github.com/knitli/${appName.toLowerCase()}`
423
+ }
512
424
  ],
513
425
  components: {
514
426
  Footer: "@knitli/docs-components/Footer.astro",
515
- PageFrame: "@knitli/docs-components/PageFrame.astro",
427
+ PageFrame: "@knitli/docs-components/PageFrame.astro"
516
428
  },
517
429
  customCss: [variables, docsStyle, `${rootDir}/src/styles/custom.css`],
518
430
  head: [
@@ -520,32 +432,34 @@ export default function createConfig(options: DocsTemplateOptions) {
520
432
  tag: "meta",
521
433
  attrs: {
522
434
  property: "og:image",
523
- content: `https://docs.knitli.com/${appName.toLowerCase()}/og-image.png`,
524
- },
435
+ content: `https://docs.knitli.com/${appName.toLowerCase()}/og-image.png`
436
+ }
525
437
  },
526
438
  {
527
439
  tag: "meta",
528
440
  attrs: {
529
441
  property: "twitter:card",
530
- content: "summary_large_image",
531
- },
532
- },
442
+ content: "summary_large_image"
443
+ }
444
+ }
533
445
  ],
534
446
  sidebar: sidebarConfig || [
535
447
  {
536
448
  label: "Guides",
537
- autogenerate: { directory: "guides" },
449
+ autogenerate: { directory: "guides" }
538
450
  },
539
451
  {
540
452
  label: "Examples",
541
- autogenerate: { directory: "examples" },
453
+ autogenerate: { directory: "examples" }
542
454
  },
543
455
  {
544
456
  label: "Reference",
545
- autogenerate: { directory: "reference" },
546
- },
547
- ],
548
- }),
549
- ],
457
+ autogenerate: { directory: "reference" }
458
+ }
459
+ ]
460
+ })
461
+ ]
550
462
  });
551
463
  }
464
+
465
+ export { createConfig as default, docsStyle, faviconIco, faviconSvg, headlineLogoDark, headlineLogoLight, variables };
@@ -0,0 +1,69 @@
1
+ export { type DocsTemplateOptions, default as createConfig } from "./config.js";
2
+ export interface InitOptions {
3
+ /** Display name for the product (e.g. "Recoco", "CodeWeaver") */
4
+ appName: string;
5
+ /** npm package name (e.g. "@knitli-site/recoco-docs") */
6
+ name: string;
7
+ /** Short product description */
8
+ description: string;
9
+ /** Cloudflare Worker name (e.g. "recoco-docs") */
10
+ workerName?: string;
11
+ /** Whether this is a CodeWeaver-branded site */
12
+ is_codeweaver?: boolean;
13
+ }
14
+ export interface AddPiecesOptions extends InitOptions {
15
+ pieces: PieceName[];
16
+ /** Overwrite existing files (default: false — skips with a warning) */
17
+ force?: boolean;
18
+ }
19
+ export declare const PIECES: {
20
+ readonly config: {
21
+ readonly description: "Astro config using createConfig()";
22
+ readonly paths: readonly ["astro.config.ts"];
23
+ };
24
+ readonly wrangler: {
25
+ readonly description: "Cloudflare Worker deployment config";
26
+ readonly paths: readonly ["wrangler.jsonc"];
27
+ };
28
+ readonly tsconfig: {
29
+ readonly description: "TypeScript config extending shared base";
30
+ readonly paths: readonly ["tsconfig.json"];
31
+ };
32
+ readonly content: {
33
+ readonly description: "Content collections, env types, and tags";
34
+ readonly paths: readonly ["src/content.config.ts", "src/env.d.ts", "tags.yml"];
35
+ };
36
+ readonly mise: {
37
+ readonly description: "mise dev tasks (build, deploy, clean, etc.)";
38
+ readonly paths: readonly ["mise.toml"];
39
+ };
40
+ readonly styles: {
41
+ readonly description: "Custom CSS hook for site-specific styles";
42
+ readonly paths: readonly ["src/styles/custom.css"];
43
+ };
44
+ readonly deps: {
45
+ readonly description: "package.json with template dependencies";
46
+ readonly paths: readonly ["package.json"];
47
+ };
48
+ readonly "starter-content": {
49
+ readonly description: "Example documentation pages";
50
+ readonly paths: readonly ["src/content/docs"];
51
+ };
52
+ };
53
+ export type PieceName = keyof typeof PIECES;
54
+ export declare const PIECE_NAMES: PieceName[];
55
+ /**
56
+ * Initialize a new Knitli docs site from the template scaffolding.
57
+ *
58
+ * Copies all files from the scaffolding directory to the target path,
59
+ * applying placeholder substitution to text files.
60
+ */
61
+ export declare function initDocsTemplate(targetPath: string, options: InitOptions): string[];
62
+ /**
63
+ * Add specific pieces from the template scaffolding to an existing project.
64
+ *
65
+ * Unlike initDocsTemplate, this only copies the files belonging to the
66
+ * requested pieces and skips existing files unless force is set.
67
+ */
68
+ export declare function addPieces(targetPath: string, options: AddPiecesOptions): string[];
69
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,mBAAmB,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,aAAa,CAAC;AAOhF,MAAM,WAAW,WAAW;IAC1B,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,gBAAiB,SAAQ,WAAW;IACnD,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,uEAAuE;IACvE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAID,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCT,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,MAAM,CAAC;AAC5C,eAAO,MAAM,WAAW,EAA0B,SAAS,EAAE,CAAC;AAyF9D;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,WAAW,GACnB,MAAM,EAAE,CAWV;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,gBAAgB,GACxB,MAAM,EAAE,CAyCV"}
@@ -1,89 +1,46 @@
1
- // SPDX-FileCopyrightText: 2026 Knitli Inc.
2
- //
3
- // SPDX-License-Identifier: Apache-2.0
4
-
5
- import {
6
- cpSync,
7
- existsSync,
8
- mkdirSync,
9
- readdirSync,
10
- readFileSync,
11
- statSync,
12
- writeFileSync,
13
- } from "node:fs";
14
- import { dirname, join, resolve } from "node:path";
15
- import { fileURLToPath } from "node:url";
16
-
17
- export { type DocsTemplateOptions, default as createConfig } from "./config.js";
18
-
19
- const __dirname = dirname(fileURLToPath(import.meta.url));
20
- const SCAFFOLDING_DIR = resolve(__dirname, "../scaffolding");
21
-
22
- // ── Options ──
23
-
24
- export interface InitOptions {
25
- /** Display name for the product (e.g. "Recoco", "CodeWeaver") */
26
- appName: string;
27
- /** npm package name (e.g. "@knitli-site/recoco-docs") */
28
- name: string;
29
- /** Short product description */
30
- description: string;
31
- /** Cloudflare Worker name (e.g. "recoco-docs") */
32
- workerName?: string;
33
- /** Whether this is a CodeWeaver-branded site */
34
- is_codeweaver?: boolean;
35
- }
36
-
37
- export interface AddPiecesOptions extends InitOptions {
38
- pieces: PieceName[];
39
- /** Overwrite existing files (default: false — skips with a warning) */
40
- force?: boolean;
41
- }
42
-
43
- // ── Piece definitions ──
44
-
45
- export const PIECES = {
1
+ import { existsSync, statSync, mkdirSync, readdirSync, readFileSync, writeFileSync, cpSync } from 'node:fs';
2
+ import { dirname, resolve, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ export { default as createConfig } from './config.js';
5
+
6
+ const __dirname$1 = dirname(fileURLToPath(import.meta.url));
7
+ const SCAFFOLDING_DIR = resolve(__dirname$1, "../scaffolding");
8
+ const PIECES = {
46
9
  config: {
47
10
  description: "Astro config using createConfig()",
48
- paths: ["astro.config.ts"],
11
+ paths: ["astro.config.ts"]
49
12
  },
50
13
  wrangler: {
51
14
  description: "Cloudflare Worker deployment config",
52
- paths: ["wrangler.jsonc"],
15
+ paths: ["wrangler.jsonc"]
53
16
  },
54
17
  tsconfig: {
55
18
  description: "TypeScript config extending shared base",
56
- paths: ["tsconfig.json"],
19
+ paths: ["tsconfig.json"]
57
20
  },
58
21
  content: {
59
22
  description: "Content collections, env types, and tags",
60
- paths: ["src/content.config.ts", "src/env.d.ts", "tags.yml"],
23
+ paths: ["src/content.config.ts", "src/env.d.ts", "tags.yml"]
61
24
  },
62
25
  mise: {
63
26
  description: "mise dev tasks (build, deploy, clean, etc.)",
64
- paths: ["mise.toml"],
27
+ paths: ["mise.toml"]
65
28
  },
66
29
  styles: {
67
30
  description: "Custom CSS hook for site-specific styles",
68
- paths: ["src/styles/custom.css"],
31
+ paths: ["src/styles/custom.css"]
69
32
  },
70
33
  deps: {
71
34
  description: "package.json with template dependencies",
72
- paths: ["package.json"],
35
+ paths: ["package.json"]
73
36
  },
74
37
  "starter-content": {
75
38
  description: "Example documentation pages",
76
- paths: ["src/content/docs"],
77
- },
78
- } as const;
79
-
80
- export type PieceName = keyof typeof PIECES;
81
- export const PIECE_NAMES = Object.keys(PIECES) as PieceName[];
82
-
83
- // ── Shared helpers ──
84
-
85
- /** File extensions that should have placeholder substitution applied */
86
- const TEXT_EXTENSIONS = new Set([
39
+ paths: ["src/content/docs"]
40
+ }
41
+ };
42
+ const PIECE_NAMES = Object.keys(PIECES);
43
+ const TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
87
44
  ".ts",
88
45
  ".js",
89
46
  ".mjs",
@@ -96,42 +53,28 @@ const TEXT_EXTENSIONS = new Set([
96
53
  ".css",
97
54
  ".html",
98
55
  ".astro",
99
- ".toml",
56
+ ".toml"
100
57
  ]);
101
-
102
- function isTextFile(filePath: string): boolean {
58
+ function isTextFile(filePath) {
103
59
  return TEXT_EXTENSIONS.has(filePath.slice(filePath.lastIndexOf(".")));
104
60
  }
105
-
106
- function buildReplacements(options: InitOptions): Record<string, string> {
61
+ function buildReplacements(options) {
107
62
  return {
108
63
  "{{appName}}": options.appName,
109
64
  "{{appNameLower}}": options.appName.toLowerCase(),
110
65
  "{{name}}": options.name,
111
66
  "{{description}}": options.description,
112
- "{{workerName}}":
113
- options.workerName ?? `${options.appName.toLowerCase()}-docs`,
67
+ "{{workerName}}": options.workerName ?? `${options.appName.toLowerCase()}-docs`
114
68
  };
115
69
  }
116
-
117
- function applyReplacements(
118
- content: string,
119
- replacements: Record<string, string>,
120
- ): string {
70
+ function applyReplacements(content, replacements) {
121
71
  let result = content;
122
72
  for (const [placeholder, value] of Object.entries(replacements)) {
123
73
  result = result.replaceAll(placeholder, value);
124
74
  }
125
75
  return result;
126
76
  }
127
-
128
- function copyFile(
129
- srcPath: string,
130
- destPath: string,
131
- replacements: Record<string, string>,
132
- created: string[],
133
- force?: boolean,
134
- ): void {
77
+ function copyFile(srcPath, destPath, replacements, created, force) {
135
78
  if (!force && existsSync(destPath)) {
136
79
  console.log(` skip ${destPath} (exists, use --force to overwrite)`);
137
80
  return;
@@ -145,14 +88,7 @@ function copyFile(
145
88
  }
146
89
  created.push(destPath);
147
90
  }
148
-
149
- function copyDirRecursive(
150
- srcDir: string,
151
- destDir: string,
152
- replacements: Record<string, string>,
153
- created: string[],
154
- force?: boolean,
155
- ): void {
91
+ function copyDirRecursive(srcDir, destDir, replacements, created, force) {
156
92
  mkdirSync(destDir, { recursive: true });
157
93
  for (const entry of readdirSync(srcDir)) {
158
94
  const srcPath = join(srcDir, entry);
@@ -164,73 +100,51 @@ function copyDirRecursive(
164
100
  }
165
101
  }
166
102
  }
167
-
168
- // ── Public API ──
169
-
170
- /**
171
- * Initialize a new Knitli docs site from the template scaffolding.
172
- *
173
- * Copies all files from the scaffolding directory to the target path,
174
- * applying placeholder substitution to text files.
175
- */
176
- export function initDocsTemplate(
177
- targetPath: string,
178
- options: InitOptions,
179
- ): string[] {
103
+ function initDocsTemplate(targetPath, options) {
180
104
  const target = resolve(targetPath);
181
105
  const replacements = buildReplacements(options);
182
- const created: string[] = [];
183
-
106
+ const created = [];
184
107
  if (!existsSync(SCAFFOLDING_DIR)) {
185
108
  throw new Error(`Scaffolding directory not found at ${SCAFFOLDING_DIR}`);
186
109
  }
187
-
188
110
  copyDirRecursive(SCAFFOLDING_DIR, target, replacements, created, true);
189
111
  return created;
190
112
  }
191
-
192
- /**
193
- * Add specific pieces from the template scaffolding to an existing project.
194
- *
195
- * Unlike initDocsTemplate, this only copies the files belonging to the
196
- * requested pieces and skips existing files unless force is set.
197
- */
198
- export function addPieces(
199
- targetPath: string,
200
- options: AddPiecesOptions,
201
- ): string[] {
113
+ function addPieces(targetPath, options) {
202
114
  const target = resolve(targetPath);
203
115
  const replacements = buildReplacements(options);
204
- const created: string[] = [];
205
-
116
+ const created = [];
206
117
  if (!existsSync(SCAFFOLDING_DIR)) {
207
118
  throw new Error(`Scaffolding directory not found at ${SCAFFOLDING_DIR}`);
208
119
  }
209
-
210
120
  for (const piece of options.pieces) {
211
121
  const pieceDef = PIECES[piece];
212
122
  if (!pieceDef) {
213
123
  throw new Error(
214
- `Unknown piece: ${piece}. Available: ${PIECE_NAMES.join(", ")}`,
124
+ `Unknown piece: ${piece}. Available: ${PIECE_NAMES.join(", ")}`
215
125
  );
216
126
  }
217
-
218
127
  for (const relPath of pieceDef.paths) {
219
128
  const srcPath = join(SCAFFOLDING_DIR, relPath);
220
129
  const destPath = join(target, relPath);
221
-
222
130
  if (!existsSync(srcPath)) {
223
131
  console.warn(` warn: ${relPath} not found in scaffolding, skipping`);
224
132
  continue;
225
133
  }
226
-
227
134
  if (statSync(srcPath).isDirectory()) {
228
- copyDirRecursive(srcPath, destPath, replacements, created, options.force);
135
+ copyDirRecursive(
136
+ srcPath,
137
+ destPath,
138
+ replacements,
139
+ created,
140
+ options.force
141
+ );
229
142
  } else {
230
143
  copyFile(srcPath, destPath, replacements, created, options.force);
231
144
  }
232
145
  }
233
146
  }
234
-
235
147
  return created;
236
148
  }
149
+
150
+ export { PIECES, PIECE_NAMES, addPieces, initDocsTemplate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knitli/astro-docs-template",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Opinionated Astro + Starlight docs site template with Knitli branding",
5
5
  "keywords": [
6
6
  "knitli",
@@ -19,20 +19,26 @@
19
19
  "author": "Knitli Inc.",
20
20
  "type": "module",
21
21
  "exports": {
22
- ".": "./src/index.ts",
23
- "./config": "./src/config.ts"
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js"
25
+ },
26
+ "./config": {
27
+ "types": "./dist/config.d.ts",
28
+ "import": "./dist/config.js"
29
+ }
24
30
  },
25
31
  "bin": {
26
- "knitli-docs": "src/cli.ts"
32
+ "knitli-docs": "dist/cli.js"
27
33
  },
28
34
  "files": [
29
- "src",
35
+ "dist",
30
36
  "scaffolding",
31
37
  "README.md",
32
38
  "CHANGELOG.md"
33
39
  ],
34
40
  "scripts": {
35
- "build": "bunx tsc --noCheck",
41
+ "build": "vite build && tsc --emitDeclarationOnly --noCheck --noEmit false",
36
42
  "prepublishOnly": "bun run build",
37
43
  "publish": "npm publish --access public",
38
44
  "typecheck": "bunx tsc --noEmit"
@@ -72,7 +78,8 @@
72
78
  "lightningcss": "catalog:optimization",
73
79
  "sharp": "catalog:astro-core",
74
80
  "svgo": "catalog:optimization",
75
- "typescript": "catalog:dev-common"
81
+ "typescript": "catalog:dev-common",
82
+ "vite": "catalog:dev-common"
76
83
  },
77
84
  "devEngines": {
78
85
  "packageManager": {
@@ -5,7 +5,9 @@
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "astro": "bunx astro",
8
- "prebuild": "test -f ./node_modules/bun/install.js && node ./node_modules/bun/install.js; bunx wrangler types; bunx astro sync",
8
+ "prepare": "test -f ./node_modules/bun/install.js && node ./node_modules/bun/install.js || true",
9
+
10
+ "prebuild": "bunx wrangler types; bunx astro sync",
9
11
  "build": "bunx astro build",
10
12
  "deploy": "bun run build && bunx wrangler deploy",
11
13
  "dev": "bunx astro dev",