@shipsite.dev/core 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.
@@ -0,0 +1,3 @@
1
+ import type { MetadataRoute } from 'next';
2
+ export default function sitemap(): MetadataRoute.Sitemap;
3
+ //# sourceMappingURL=sitemap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sitemap.d.ts","sourceRoot":"","sources":["../src/sitemap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AA+C1C,MAAM,CAAC,OAAO,UAAU,OAAO,IAAI,aAAa,CAAC,OAAO,CAwCvD"}
@@ -0,0 +1,65 @@
1
+ import { allSitePages } from 'content-collections';
2
+ import { getAllPages, buildCanonicalUrl, isNoIndexPage } from './pages';
3
+ import { getDefaultLocale } from './config';
4
+ const priorityMap = {
5
+ landing: 1.0,
6
+ product: 0.9,
7
+ 'blog-index': 0.8,
8
+ 'blog-article': 0.7,
9
+ 'feature-detail': 0.8,
10
+ comparison: 0.8,
11
+ page: 0.6,
12
+ legal: 0.3,
13
+ };
14
+ const changefreqMap = {
15
+ landing: 'weekly',
16
+ product: 'weekly',
17
+ 'blog-index': 'daily',
18
+ 'blog-article': 'monthly',
19
+ 'feature-detail': 'monthly',
20
+ comparison: 'monthly',
21
+ page: 'monthly',
22
+ legal: 'yearly',
23
+ };
24
+ function resolveSlug(page, locale) {
25
+ const doc = allSitePages.find((d) => d.contentFolder === page.content && d.locale === locale);
26
+ if (doc?.slug) {
27
+ if (doc.slug.includes('/'))
28
+ return doc.slug;
29
+ const folderParts = doc.contentFolder.split('/');
30
+ const prefix = folderParts.length > 1 ? folderParts.slice(0, -1).join('/') + '/' : '';
31
+ return prefix + doc.slug;
32
+ }
33
+ return page.slug;
34
+ }
35
+ export default function sitemap() {
36
+ const allPages = getAllPages();
37
+ const defaultLocale = getDefaultLocale();
38
+ const entries = [];
39
+ for (const page of allPages) {
40
+ if (isNoIndexPage(page))
41
+ continue;
42
+ const pageLocales = page.locales || [defaultLocale];
43
+ let lastModified;
44
+ const contentDoc = allSitePages.find((doc) => doc.contentFolder === page.content && doc.locale === defaultLocale);
45
+ if (contentDoc?.date) {
46
+ lastModified = contentDoc.date;
47
+ }
48
+ const languages = {};
49
+ for (const locale of pageLocales) {
50
+ languages[locale] = buildCanonicalUrl(locale, resolveSlug(page, locale));
51
+ }
52
+ languages['x-default'] = buildCanonicalUrl(defaultLocale, resolveSlug(page, defaultLocale));
53
+ for (const locale of pageLocales) {
54
+ entries.push({
55
+ url: buildCanonicalUrl(locale, resolveSlug(page, locale)),
56
+ ...(lastModified && { lastModified }),
57
+ changeFrequency: changefreqMap[page.type] ?? 'monthly',
58
+ priority: priorityMap[page.type] ?? 0.5,
59
+ alternates: { languages },
60
+ });
61
+ }
62
+ }
63
+ return entries;
64
+ }
65
+ //# sourceMappingURL=sitemap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sitemap.js","sourceRoot":"","sources":["../src/sitemap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Generate a full color scale (50-950) from a hex color.
3
+ * Uses HSL manipulation to create lighter and darker variants.
4
+ */
5
+ export declare function hexToHsl(hex: string): [number, number, number];
6
+ export declare function hslToHex(h: number, s: number, l: number): string;
7
+ export declare function generateColorScale(hex: string): Record<string, string>;
8
+ export declare function generateCSSVariables(config: {
9
+ colors?: {
10
+ primary?: string;
11
+ accent?: string;
12
+ background?: string;
13
+ text?: string;
14
+ };
15
+ }): string;
16
+ /**
17
+ * Generate shadcn/ui-compatible CSS tokens from user colors.
18
+ * Produces a :root block with --primary, --background, --foreground, etc.
19
+ */
20
+ export declare function generateShadcnTokens(config: {
21
+ colors?: {
22
+ primary?: string;
23
+ accent?: string;
24
+ background?: string;
25
+ text?: string;
26
+ };
27
+ }): string;
28
+ //# sourceMappingURL=theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAoB9D;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAYhE;AAgBD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAStE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAC3C,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH,GAAG,MAAM,CAyBT;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAC3C,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH,GAAG,MAAM,CA6GT"}
package/dist/theme.js ADDED
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Generate a full color scale (50-950) from a hex color.
3
+ * Uses HSL manipulation to create lighter and darker variants.
4
+ */
5
+ export function hexToHsl(hex) {
6
+ const r = parseInt(hex.slice(1, 3), 16) / 255;
7
+ const g = parseInt(hex.slice(3, 5), 16) / 255;
8
+ const b = parseInt(hex.slice(5, 7), 16) / 255;
9
+ const max = Math.max(r, g, b);
10
+ const min = Math.min(r, g, b);
11
+ const l = (max + min) / 2;
12
+ if (max === min)
13
+ return [0, 0, l * 100];
14
+ const d = max - min;
15
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
16
+ let h = 0;
17
+ if (max === r)
18
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
19
+ else if (max === g)
20
+ h = ((b - r) / d + 2) / 6;
21
+ else
22
+ h = ((r - g) / d + 4) / 6;
23
+ return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)];
24
+ }
25
+ export function hslToHex(h, s, l) {
26
+ s /= 100;
27
+ l /= 100;
28
+ const a = s * Math.min(l, 1 - l);
29
+ const f = (n) => {
30
+ const k = (n + h / 30) % 12;
31
+ const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
32
+ return Math.round(255 * color)
33
+ .toString(16)
34
+ .padStart(2, '0');
35
+ };
36
+ return `#${f(0)}${f(8)}${f(4)}`;
37
+ }
38
+ const SCALE_STEPS = {
39
+ 50: 96,
40
+ 100: 91,
41
+ 200: 82,
42
+ 300: 71,
43
+ 400: 60,
44
+ 500: 50,
45
+ 600: 42,
46
+ 700: 34,
47
+ 800: 26,
48
+ 900: 20,
49
+ 950: 14,
50
+ };
51
+ export function generateColorScale(hex) {
52
+ const [h, s] = hexToHsl(hex);
53
+ const scale = {};
54
+ for (const [step, lightness] of Object.entries(SCALE_STEPS)) {
55
+ scale[step] = hslToHex(h, s, lightness);
56
+ }
57
+ return scale;
58
+ }
59
+ export function generateCSSVariables(config) {
60
+ const colors = config.colors || {};
61
+ const primary = colors.primary || '#5d5bd4';
62
+ const accent = colors.accent || '#067647';
63
+ const background = colors.background || '#ffffff';
64
+ const text = colors.text || '#1f2a37';
65
+ const primaryScale = generateColorScale(primary);
66
+ const accentScale = generateColorScale(accent);
67
+ let css = ':root {\n';
68
+ css += ` --ss-primary: ${primary};\n`;
69
+ css += ` --ss-accent: ${accent};\n`;
70
+ css += ` --ss-background: ${background};\n`;
71
+ css += ` --ss-text: ${text};\n`;
72
+ for (const [step, color] of Object.entries(primaryScale)) {
73
+ css += ` --ss-primary-${step}: ${color};\n`;
74
+ }
75
+ for (const [step, color] of Object.entries(accentScale)) {
76
+ css += ` --ss-accent-${step}: ${color};\n`;
77
+ }
78
+ css += '}\n';
79
+ return css;
80
+ }
81
+ /**
82
+ * Generate shadcn/ui-compatible CSS tokens from user colors.
83
+ * Produces a :root block with --primary, --background, --foreground, etc.
84
+ */
85
+ export function generateShadcnTokens(config) {
86
+ const colors = config.colors || {};
87
+ const primary = colors.primary || '#5d5bd4';
88
+ const accent = colors.accent || '#067647';
89
+ const background = colors.background || '#ffffff';
90
+ const text = colors.text || '#1f2a37';
91
+ const [pH, pS, pL] = hexToHsl(primary);
92
+ const [aH, aS, aL] = hexToHsl(accent);
93
+ const [bH, bS, bL] = hexToHsl(background);
94
+ const [tH, tS, tL] = hexToHsl(text);
95
+ // Generate derived colors
96
+ const primaryFg = pL > 50 ? `${tH} ${tS}% 15%` : `${bH} ${bS}% 98%`;
97
+ const brandFg = `${pH} ${Math.min(pS + 10, 100)}% ${Math.min(pL + 10, 85)}%`;
98
+ // Muted: desaturated, lighter version of background
99
+ const mutedS = Math.max(bS - 2, 0);
100
+ const muted = `${bH} ${mutedS}% 93%`;
101
+ const mutedFg = `${tH} ${Math.max(tS - 5, 0)}% 45%`;
102
+ // Card: slightly different from background
103
+ const card = `${bH} ${bS}% 100%`;
104
+ const cardFg = `${tH} ${tS}% ${tL}%`;
105
+ // Border/input
106
+ const borderL = Math.max(bL - 8, 0);
107
+ const border = `${bH} ${Math.max(bS - 3, 0)}% ${borderL}%`;
108
+ const input = border;
109
+ const ring = `${tH} ${Math.max(tS - 5, 0)}% 70%`;
110
+ // Secondary
111
+ const secondary = muted;
112
+ const secondaryFg = `${tH} ${tS}% 20%`;
113
+ // Accent (shadcn meaning: hover background)
114
+ const accentBg = `${bH} ${mutedS}% 90%`;
115
+ const accentFg = `${tH} ${tS}% 20%`;
116
+ // Destructive
117
+ const destructive = `0 72% 51%`;
118
+ const destructiveFg = `0 72% 51%`;
119
+ // Popover = card
120
+ const popover = card;
121
+ const popoverFg = cardFg;
122
+ // Shadow
123
+ const shadow = '#00000008';
124
+ return `:root {
125
+ /* Brand colors */
126
+ --brand: hsl(${pH} ${pS}% ${pL}%);
127
+ --brand-foreground: hsl(${brandFg});
128
+
129
+ /* Primary */
130
+ --primary: hsl(${pH} ${pS}% ${pL}%);
131
+ --primary-foreground: hsl(${primaryFg});
132
+
133
+ /* Background & Foreground */
134
+ --background: hsl(${bH} ${bS}% ${bL}%);
135
+ --foreground: hsl(${tH} ${tS}% ${tL}%);
136
+
137
+ /* Card */
138
+ --card: hsl(${card});
139
+ --card-foreground: hsl(${cardFg});
140
+
141
+ /* Popover */
142
+ --popover: hsl(${popover});
143
+ --popover-foreground: hsl(${popoverFg});
144
+
145
+ /* Secondary */
146
+ --secondary: hsl(${secondary});
147
+ --secondary-foreground: hsl(${secondaryFg});
148
+
149
+ /* Muted */
150
+ --muted: hsl(${muted});
151
+ --muted-foreground: hsl(${mutedFg});
152
+
153
+ /* Accent */
154
+ --accent: hsl(${accentBg});
155
+ --accent-foreground: hsl(${accentFg});
156
+
157
+ /* Destructive */
158
+ --destructive: hsl(${destructive});
159
+ --destructive-foreground: hsl(${destructiveFg});
160
+
161
+ /* Borders & Input */
162
+ --border: hsl(${border});
163
+ --input: hsl(${input});
164
+ --ring: hsl(${ring});
165
+
166
+ /* Radius */
167
+ --radius: 0.625rem;
168
+
169
+ /* Line width for Launch UI utilities */
170
+ --line-width: 1px;
171
+
172
+ /* Shadows */
173
+ --shadow: ${shadow};
174
+ --shadow-strong: #00000010;
175
+
176
+ /* Legacy --ss-* tokens (keep for backwards compat during transition) */
177
+ --ss-primary: ${primary};
178
+ --ss-accent: ${accent};
179
+ --ss-background: ${background};
180
+ --ss-text: ${text};
181
+ }
182
+ `;
183
+ }
184
+ //# sourceMappingURL=theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.js","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@shipsite.dev/core",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "./pages": {
13
+ "types": "./dist/pages.d.ts",
14
+ "default": "./dist/pages.js"
15
+ },
16
+ "./mdx": {
17
+ "types": "./dist/mdx.d.ts",
18
+ "default": "./dist/mdx.js"
19
+ },
20
+ "./blog": {
21
+ "types": "./dist/blog.d.ts",
22
+ "default": "./dist/blog.js"
23
+ },
24
+ "./config": {
25
+ "types": "./dist/config.d.ts",
26
+ "default": "./dist/config.js"
27
+ },
28
+ "./sitemap": {
29
+ "types": "./dist/sitemap.d.ts",
30
+ "default": "./dist/sitemap.js"
31
+ },
32
+ "./middleware": {
33
+ "types": "./dist/middleware.d.ts",
34
+ "default": "./dist/middleware.js"
35
+ },
36
+ "./generate-slug-map": "./src/generate-slug-map.mjs",
37
+ "./content-collections": {
38
+ "types": "./dist/content-collections.d.ts",
39
+ "default": "./dist/content-collections.js"
40
+ },
41
+ "./theme": {
42
+ "types": "./dist/theme.d.ts",
43
+ "default": "./dist/theme.js"
44
+ }
45
+ },
46
+ "files": [
47
+ "dist/",
48
+ "src/generate-slug-map.mjs"
49
+ ],
50
+ "scripts": {
51
+ "build": "tsc",
52
+ "dev": "tsc --watch",
53
+ "clean": "rm -rf dist"
54
+ },
55
+ "dependencies": {
56
+ "@content-collections/core": "^0.14.1",
57
+ "@content-collections/next": "^0.2.11",
58
+ "next": "16.1.6",
59
+ "next-intl": "^4.6.1",
60
+ "next-mdx-remote": "^6.0.0",
61
+ "react": "19.2.3",
62
+ "react-dom": "19.2.3",
63
+ "zod": "^3.24.0"
64
+ },
65
+ "devDependencies": {
66
+ "typescript": "^5.8.3",
67
+ "@types/react": "^19",
68
+ "@types/node": "^20"
69
+ },
70
+ "peerDependencies": {
71
+ "@shipsite.dev/components": "^0.1.0"
72
+ }
73
+ }
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Generate slug-map.json for middleware slug remapping.
4
+ * Maps EN slugs → localized slugs for each non-default locale.
5
+ */
6
+
7
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
8
+ import { join, dirname } from 'path';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ export function generateSlugMap(rootDir) {
12
+ const configPath = join(rootDir, 'shipsite.json');
13
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'));
14
+ const defaultLocale = config.i18n?.defaultLocale || 'en';
15
+ const pages = config.pages || [];
16
+
17
+ function extractSlug(mdxPath) {
18
+ if (!existsSync(mdxPath)) return undefined;
19
+ const content = readFileSync(mdxPath, 'utf-8');
20
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
21
+ if (!fmMatch) return undefined;
22
+ const fm = fmMatch[1];
23
+ const slugMatch =
24
+ fm.match(/^slug:\s*"(.*)"\s*$/m) || fm.match(/^slug:\s*'(.*)'\s*$/m);
25
+ return slugMatch ? slugMatch[1] : undefined;
26
+ }
27
+
28
+ function resolveDocSlug(slug, contentFolder, fallbackSlug) {
29
+ if (!slug) return fallbackSlug;
30
+ if (slug.includes('/')) return slug;
31
+ const folderParts = contentFolder.split('/');
32
+ const prefix =
33
+ folderParts.length > 1 ? folderParts.slice(0, -1).join('/') + '/' : '';
34
+ return prefix + slug;
35
+ }
36
+
37
+ const slugMap = {};
38
+
39
+ for (const page of pages) {
40
+ const pageLocales = page.locales || [defaultLocale];
41
+ const enSlug = page.slug;
42
+ const localizedSlugs = {};
43
+
44
+ for (const locale of pageLocales) {
45
+ if (locale === defaultLocale) continue;
46
+
47
+ const mdxPath = join(rootDir, 'content', page.content, `${locale}.mdx`);
48
+ const frontmatterSlug = extractSlug(mdxPath);
49
+ const resolvedSlug = resolveDocSlug(
50
+ frontmatterSlug,
51
+ page.content,
52
+ enSlug,
53
+ );
54
+
55
+ if (resolvedSlug !== enSlug) {
56
+ localizedSlugs[locale] = resolvedSlug;
57
+ }
58
+ }
59
+
60
+ if (Object.keys(localizedSlugs).length > 0) {
61
+ slugMap[enSlug] = localizedSlugs;
62
+ }
63
+ }
64
+
65
+ return slugMap;
66
+ }
67
+
68
+ // CLI usage
69
+ const __filename = fileURLToPath(import.meta.url);
70
+ if (
71
+ process.argv[1] &&
72
+ (process.argv[1] === __filename ||
73
+ process.argv[1].endsWith('generate-slug-map.mjs'))
74
+ ) {
75
+ const rootDir = process.argv[2] || process.cwd();
76
+ const slugMap = generateSlugMap(rootDir);
77
+ const outputPath = join(rootDir, '.shipsite', 'slug-map.json');
78
+ writeFileSync(outputPath, JSON.stringify(slugMap, null, 2) + '\n');
79
+ console.log(
80
+ `Generated slug-map.json with ${Object.keys(slugMap).length} entries`,
81
+ );
82
+ }