@compilr-dev/factory 0.1.8 → 0.1.9

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.
@@ -26,5 +26,7 @@ export const defaultRegistry = new ToolkitRegistry();
26
26
  // Register built-in toolkits
27
27
  import { reactNodeToolkit } from '../toolkits/react-node/index.js';
28
28
  import { nextPrismaToolkit } from '../toolkits/next-prisma/index.js';
29
+ import { staticLandingToolkit } from '../toolkits/static-landing/index.js';
29
30
  defaultRegistry.register(reactNodeToolkit);
30
31
  defaultRegistry.register(nextPrismaToolkit);
32
+ defaultRegistry.register(staticLandingToolkit);
package/dist/index.d.ts CHANGED
@@ -20,4 +20,5 @@ export { writeFactoryFiles } from './factory/file-writer.js';
20
20
  export type { WriteResult } from './factory/file-writer.js';
21
21
  export { reactNodeToolkit } from './toolkits/react-node/index.js';
22
22
  export { nextPrismaToolkit } from './toolkits/next-prisma/index.js';
23
+ export { staticLandingToolkit } from './toolkits/static-landing/index.js';
23
24
  export { factoryScaffoldSkill, factorySkills } from './factory/skill.js';
package/dist/index.js CHANGED
@@ -23,5 +23,7 @@ export { writeFactoryFiles } from './factory/file-writer.js';
23
23
  export { reactNodeToolkit } from './toolkits/react-node/index.js';
24
24
  // Next.js + Prisma Toolkit
25
25
  export { nextPrismaToolkit } from './toolkits/next-prisma/index.js';
26
+ // Static Landing Page Toolkit
27
+ export { staticLandingToolkit } from './toolkits/static-landing/index.js';
26
28
  // Factory skill (Phase 5)
27
29
  export { factoryScaffoldSkill, factorySkills } from './factory/skill.js';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Static Landing Page Toolkit — Configuration Files Generator
3
+ *
4
+ * Generates: package.json, tailwind.config.js, postcss.config.js
5
+ */
6
+ import type { ApplicationModel } from '../../model/types.js';
7
+ import type { FactoryFile } from '../types.js';
8
+ export declare function generateConfigFiles(model: ApplicationModel): FactoryFile[];
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Static Landing Page Toolkit — Configuration Files Generator
3
+ *
4
+ * Generates: package.json, tailwind.config.js, postcss.config.js
5
+ */
6
+ import { toKebabCase } from '../../model/naming.js';
7
+ import { generateColorShades } from '../shared/color-utils.js';
8
+ export function generateConfigFiles(model) {
9
+ const appSlug = toKebabCase(model.identity.name);
10
+ return [generatePackageJson(appSlug), generateTailwindConfig(model), generatePostCssConfig()];
11
+ }
12
+ function generatePackageJson(appSlug) {
13
+ const pkg = {
14
+ name: appSlug,
15
+ version: '0.1.0',
16
+ private: true,
17
+ scripts: {
18
+ dev: 'concurrently "npx tailwindcss -i ./src/input.css -o ./dist/style.css --watch" "npx serve . -l 3000"',
19
+ build: 'npx tailwindcss -i ./src/input.css -o ./dist/style.css --minify',
20
+ preview: 'npx serve . -l 3000',
21
+ },
22
+ devDependencies: {
23
+ autoprefixer: '^10.4.20',
24
+ concurrently: '^9.1.0',
25
+ postcss: '^8.4.49',
26
+ serve: '^14.2.4',
27
+ tailwindcss: '^3.4.15',
28
+ },
29
+ };
30
+ return { path: 'package.json', content: JSON.stringify(pkg, null, 2) + '\n' };
31
+ }
32
+ function generateTailwindConfig(model) {
33
+ const darkLine = model.features.darkMode ? ` darkMode: 'class',\n` : '';
34
+ const shades = generateColorShades(model.theme.primaryColor);
35
+ return {
36
+ path: 'tailwind.config.js',
37
+ content: `/** @type {import('tailwindcss').Config} */
38
+ export default {
39
+ ${darkLine} content: ['./*.html'],
40
+ theme: {
41
+ extend: {
42
+ colors: {
43
+ primary: ${shades},
44
+ },
45
+ },
46
+ },
47
+ plugins: [],
48
+ };
49
+ `,
50
+ };
51
+ }
52
+ function generatePostCssConfig() {
53
+ return {
54
+ path: 'postcss.config.js',
55
+ content: `export default {
56
+ plugins: {
57
+ tailwindcss: {},
58
+ autoprefixer: {},
59
+ },
60
+ };
61
+ `,
62
+ };
63
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Static Landing Page Toolkit
3
+ *
4
+ * Generates a responsive landing page: HTML + Tailwind CSS + minimal JavaScript.
5
+ * Deterministic: same ApplicationModel → same output files.
6
+ */
7
+ import type { FactoryToolkit } from '../types.js';
8
+ export declare const staticLandingToolkit: FactoryToolkit;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Static Landing Page Toolkit
3
+ *
4
+ * Generates a responsive landing page: HTML + Tailwind CSS + minimal JavaScript.
5
+ * Deterministic: same ApplicationModel → same output files.
6
+ */
7
+ import { generateConfigFiles } from './config.js';
8
+ import { generateStaticFiles } from './static.js';
9
+ import { generatePages } from './pages.js';
10
+ export const staticLandingToolkit = {
11
+ id: 'static-landing',
12
+ name: 'Static Landing Page',
13
+ description: 'HTML + Tailwind CSS — responsive marketing/landing page with minimal JavaScript',
14
+ requiredSections: ['identity', 'theme'],
15
+ generate(model) {
16
+ const warnings = [];
17
+ const allFiles = [];
18
+ const generators = [
19
+ generateConfigFiles(model),
20
+ generateStaticFiles(model),
21
+ generatePages(model),
22
+ ];
23
+ for (const files of generators) {
24
+ allFiles.push(...files);
25
+ }
26
+ if (model.entities.length === 0) {
27
+ warnings.push('No entities defined — features section will use placeholder content.');
28
+ }
29
+ return {
30
+ files: allFiles,
31
+ toolkit: 'static-landing',
32
+ warnings,
33
+ };
34
+ },
35
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Static Landing Page Toolkit — Page Files Generator
3
+ *
4
+ * Generates: index.html, 404.html
5
+ */
6
+ import type { ApplicationModel } from '../../model/types.js';
7
+ import type { FactoryFile } from '../types.js';
8
+ export declare function generatePages(model: ApplicationModel): FactoryFile[];
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Static Landing Page Toolkit — Page Files Generator
3
+ *
4
+ * Generates: index.html, 404.html
5
+ */
6
+ export function generatePages(model) {
7
+ return [generateIndexHtml(model), generate404Html(model)];
8
+ }
9
+ function generateFeatureCards(model) {
10
+ if (model.entities.length > 0) {
11
+ return model.entities
12
+ .map((entity) => ` <div class="bg-white p-6 shadow-sm dark:bg-gray-800">
13
+ <div class="mb-4 text-4xl">${entity.icon}</div>
14
+ <h3 class="mb-2 text-xl font-bold text-gray-900 dark:text-white">${entity.name}</h3>
15
+ <p class="text-gray-600 dark:text-gray-300">${entity.description ?? `Manage your ${entity.pluralName.toLowerCase()} efficiently.`}</p>
16
+ </div>`)
17
+ .join('\n');
18
+ }
19
+ const placeholders = [
20
+ {
21
+ icon: '⚡',
22
+ title: 'Fast & Reliable',
23
+ desc: 'Built for speed with optimized performance out of the box.',
24
+ },
25
+ {
26
+ icon: '🔒',
27
+ title: 'Secure by Default',
28
+ desc: 'Security best practices baked in from the start.',
29
+ },
30
+ {
31
+ icon: '🎯',
32
+ title: 'Easy to Use',
33
+ desc: 'Intuitive interface designed for the best user experience.',
34
+ },
35
+ ];
36
+ return placeholders
37
+ .map((p) => ` <div class="bg-white p-6 shadow-sm dark:bg-gray-800">
38
+ <div class="mb-4 text-4xl">${p.icon}</div>
39
+ <h3 class="mb-2 text-xl font-bold text-gray-900 dark:text-white">${p.title}</h3>
40
+ <p class="text-gray-600 dark:text-gray-300">${p.desc}</p>
41
+ </div>`)
42
+ .join('\n');
43
+ }
44
+ function generateDarkModeToggle() {
45
+ return `
46
+ <button id="dark-toggle" class="text-gray-600 hover:text-gray-900 dark:text-gray-300 dark:hover:text-white">
47
+ <span id="dark-icon">🌙</span>
48
+ </button>`;
49
+ }
50
+ function generateDarkModeScript() {
51
+ return `
52
+ // Dark mode toggle
53
+ const toggle = document.getElementById('dark-toggle');
54
+ const icon = document.getElementById('dark-icon');
55
+ if (localStorage.getItem('theme') === 'dark') {
56
+ document.documentElement.classList.add('dark');
57
+ icon.textContent = '☀️';
58
+ }
59
+ toggle.addEventListener('click', () => {
60
+ document.documentElement.classList.toggle('dark');
61
+ const isDark = document.documentElement.classList.contains('dark');
62
+ icon.textContent = isDark ? '☀️' : '🌙';
63
+ localStorage.setItem('theme', isDark ? 'dark' : 'light');
64
+ });`;
65
+ }
66
+ function generateCtaSection(model) {
67
+ return `
68
+ <!-- CTA -->
69
+ <section id="cta" class="bg-primary px-6 py-20 text-center text-white">
70
+ <div class="mx-auto max-w-2xl">
71
+ <h2 class="mb-4 text-3xl font-bold">Ready to get started?</h2>
72
+ <p class="mb-8 text-lg opacity-90">Join us and experience ${model.identity.name} today.</p>
73
+ <form class="mx-auto flex max-w-md gap-2">
74
+ <input
75
+ type="email"
76
+ placeholder="Enter your email"
77
+ class="flex-1 px-4 py-3 text-gray-900 placeholder-gray-500 focus:outline-none"
78
+ />
79
+ <button type="submit" class="bg-gray-900 px-6 py-3 font-semibold text-white transition-colors hover:bg-gray-800">
80
+ Sign Up
81
+ </button>
82
+ </form>
83
+ </div>
84
+ </section>
85
+ `;
86
+ }
87
+ function generateIndexHtml(model) {
88
+ const { name, description } = model.identity;
89
+ const hasDarkMode = model.features.darkMode;
90
+ const hasAuth = model.features.auth;
91
+ const darkBg = hasDarkMode ? ' dark:bg-gray-900' : '';
92
+ const darkText = hasDarkMode ? ' dark:text-white' : '';
93
+ const darkNavBg = hasDarkMode ? ' dark:bg-gray-800' : '';
94
+ const navSignUp = hasAuth
95
+ ? `\n <a href="#cta" class="bg-primary px-4 py-2 text-sm font-semibold text-white transition-colors hover:bg-primary-700">Sign Up</a>`
96
+ : '';
97
+ const darkToggle = hasDarkMode ? generateDarkModeToggle() : '';
98
+ const ctaSection = hasAuth ? generateCtaSection(model) : '';
99
+ const featureCards = generateFeatureCards(model);
100
+ const scriptParts = [];
101
+ if (hasDarkMode) {
102
+ scriptParts.push(generateDarkModeScript());
103
+ }
104
+ scriptParts.push(`
105
+ // Smooth scroll for anchor links
106
+ document.querySelectorAll('a[href^="#"]').forEach(anchor => {
107
+ anchor.addEventListener('click', (e) => {
108
+ e.preventDefault();
109
+ const target = document.querySelector(anchor.getAttribute('href'));
110
+ if (target) target.scrollIntoView({ behavior: 'smooth' });
111
+ });
112
+ });`);
113
+ const scriptTag = `\n <script>${scriptParts.join('\n')}\n </script>`;
114
+ return {
115
+ path: 'index.html',
116
+ content: `<!doctype html>
117
+ <html lang="en">
118
+ <head>
119
+ <meta charset="UTF-8" />
120
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
121
+ <meta name="description" content="${description}" />
122
+ <title>${name}</title>
123
+ <link rel="stylesheet" href="./dist/style.css" />
124
+ </head>
125
+ <body class="bg-white text-gray-900${darkBg}${darkText}">
126
+ <!-- Nav -->
127
+ <nav class="sticky top-0 z-50 bg-white px-6 py-4 shadow-sm${darkNavBg}">
128
+ <div class="mx-auto flex max-w-6xl items-center justify-between">
129
+ <a href="#" class="text-xl font-bold text-primary">${name}</a>
130
+ <div class="flex items-center gap-4">
131
+ <a href="#features" class="text-sm text-gray-600 hover:text-gray-900${hasDarkMode ? ' dark:text-gray-300 dark:hover:text-white' : ''}"
132
+ >Features</a
133
+ >${darkToggle}${navSignUp}
134
+ </div>
135
+ </div>
136
+ </nav>
137
+
138
+ <!-- Hero -->
139
+ <section id="hero" class="px-6 py-24 text-center">
140
+ <div class="mx-auto max-w-3xl">
141
+ <h1 class="mb-6 text-5xl font-bold tracking-tight">${name}</h1>
142
+ <p class="mb-8 text-xl text-gray-600${hasDarkMode ? ' dark:text-gray-300' : ''}">${description}</p>
143
+ <a href="#features" class="btn-primary">Get Started</a>
144
+ </div>
145
+ </section>
146
+
147
+ <!-- Features -->
148
+ <section id="features" class="bg-gray-50 px-6 py-20${hasDarkMode ? ' dark:bg-gray-800/50' : ''}">
149
+ <div class="mx-auto max-w-6xl">
150
+ <h2 class="mb-12 text-center text-3xl font-bold">Features</h2>
151
+ <div class="grid gap-8 sm:grid-cols-2 lg:grid-cols-3">
152
+ ${featureCards}
153
+ </div>
154
+ </div>
155
+ </section>
156
+ ${ctaSection}
157
+ <!-- Footer -->
158
+ <footer class="border-t border-gray-200 px-6 py-8${hasDarkMode ? ' dark:border-gray-700' : ''}">
159
+ <div class="mx-auto max-w-6xl text-center text-sm text-gray-500${hasDarkMode ? ' dark:text-gray-400' : ''}">
160
+ &copy; ${String(new Date().getFullYear())} ${name}. All rights reserved.
161
+ </div>
162
+ </footer>
163
+ ${scriptTag}
164
+ </body>
165
+ </html>
166
+ `,
167
+ };
168
+ }
169
+ function generate404Html(model) {
170
+ const { name } = model.identity;
171
+ return {
172
+ path: '404.html',
173
+ content: `<!doctype html>
174
+ <html lang="en">
175
+ <head>
176
+ <meta charset="UTF-8" />
177
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
178
+ <title>Page Not Found — ${name}</title>
179
+ <link rel="stylesheet" href="./dist/style.css" />
180
+ </head>
181
+ <body class="flex min-h-screen items-center justify-center bg-white text-gray-900">
182
+ <div class="text-center">
183
+ <h1 class="mb-4 text-6xl font-bold text-primary">404</h1>
184
+ <p class="mb-8 text-xl text-gray-600">Page not found</p>
185
+ <a href="/" class="btn-primary">Go back home</a>
186
+ </div>
187
+ </body>
188
+ </html>
189
+ `,
190
+ };
191
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Static Landing Page Toolkit — Static Files Generator
3
+ *
4
+ * Generates: .gitignore, README.md, src/input.css, dist/.gitkeep
5
+ */
6
+ import type { ApplicationModel } from '../../model/types.js';
7
+ import type { FactoryFile } from '../types.js';
8
+ export declare function generateStaticFiles(model: ApplicationModel): FactoryFile[];
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Static Landing Page Toolkit — Static Files Generator
3
+ *
4
+ * Generates: .gitignore, README.md, src/input.css, dist/.gitkeep
5
+ */
6
+ export function generateStaticFiles(model) {
7
+ return [generateGitignore(), generateReadme(model), generateInputCss(), generateDistGitkeep()];
8
+ }
9
+ function generateGitignore() {
10
+ return {
11
+ path: '.gitignore',
12
+ content: `node_modules
13
+ dist/style.css
14
+ `,
15
+ };
16
+ }
17
+ function generateReadme(model) {
18
+ return {
19
+ path: 'README.md',
20
+ content: `# ${model.identity.name}
21
+
22
+ ${model.identity.description}
23
+
24
+ ## Getting Started
25
+
26
+ \`\`\`bash
27
+ npm install
28
+ npm run dev
29
+ \`\`\`
30
+
31
+ ## Build
32
+
33
+ \`\`\`bash
34
+ npm run build
35
+ \`\`\`
36
+
37
+ ## Tech Stack
38
+
39
+ - HTML
40
+ - Tailwind CSS
41
+ - PostCSS + Autoprefixer
42
+ `,
43
+ };
44
+ }
45
+ function generateInputCss() {
46
+ return {
47
+ path: 'src/input.css',
48
+ content: `@tailwind base;
49
+ @tailwind components;
50
+ @tailwind utilities;
51
+
52
+ @layer components {
53
+ .btn-primary {
54
+ @apply inline-block rounded-none bg-primary px-6 py-3 font-semibold text-white transition-colors hover:bg-primary-700;
55
+ }
56
+ }
57
+ `,
58
+ };
59
+ }
60
+ function generateDistGitkeep() {
61
+ return {
62
+ path: 'dist/.gitkeep',
63
+ content: '',
64
+ };
65
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/factory",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "AI-driven application scaffolder for the compilr-dev ecosystem",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",