@adriankulik/create-fullstack-app 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +84 -0
  3. package/cli/index.js +170 -0
  4. package/cli/package-lock.json +1338 -0
  5. package/cli/package.json +23 -0
  6. package/package.json +40 -0
  7. package/templates/backend/fastapi/lint.sh +9 -0
  8. package/templates/backend/fastapi/main.py +28 -0
  9. package/templates/backend/fastapi/requirements.txt +5 -0
  10. package/templates/backend/fastapi/start.sh +7 -0
  11. package/templates/backend/fastapi/test.sh +7 -0
  12. package/templates/backend/fastapi/test_main.py +19 -0
  13. package/templates/backend/flask/lint.sh +9 -0
  14. package/templates/backend/flask/main.py +24 -0
  15. package/templates/backend/flask/requirements.txt +5 -0
  16. package/templates/backend/flask/start.sh +7 -0
  17. package/templates/backend/flask/test.sh +7 -0
  18. package/templates/backend/flask/test_main.py +21 -0
  19. package/templates/base/.github/workflows/test.yml +36 -0
  20. package/templates/base/.vscode/extensions.json +8 -0
  21. package/templates/base/GEMINI.md +65 -0
  22. package/templates/base/README.md +37 -0
  23. package/templates/base/gitignore +13 -0
  24. package/templates/base/lint.sh +10 -0
  25. package/templates/base/start.sh +13 -0
  26. package/templates/base/test.sh +10 -0
  27. package/templates/frontend/angular/angular.json +84 -0
  28. package/templates/frontend/angular/karma.conf.js +45 -0
  29. package/templates/frontend/angular/lint.sh +2 -0
  30. package/templates/frontend/angular/package-lock.json +16204 -0
  31. package/templates/frontend/angular/package.json +40 -0
  32. package/templates/frontend/angular/src/app/app.component.spec.ts +24 -0
  33. package/templates/frontend/angular/src/app/app.component.ts +73 -0
  34. package/templates/frontend/angular/src/favicon.ico +0 -0
  35. package/templates/frontend/angular/src/index.html +12 -0
  36. package/templates/frontend/angular/src/main.ts +5 -0
  37. package/templates/frontend/angular/src/styles.css +1 -0
  38. package/templates/frontend/angular/start.sh +2 -0
  39. package/templates/frontend/angular/test.sh +2 -0
  40. package/templates/frontend/angular/tsconfig.app.json +16 -0
  41. package/templates/frontend/angular/tsconfig.json +32 -0
  42. package/templates/frontend/angular/tsconfig.spec.json +14 -0
  43. package/templates/frontend/nextjs/.eslintrc.json +3 -0
  44. package/templates/frontend/nextjs/__tests__/page.test.tsx +39 -0
  45. package/templates/frontend/nextjs/app/layout.tsx +18 -0
  46. package/templates/frontend/nextjs/app/page.tsx +69 -0
  47. package/templates/frontend/nextjs/jest.config.js +16 -0
  48. package/templates/frontend/nextjs/jest.setup.js +1 -0
  49. package/templates/frontend/nextjs/lint.sh +3 -0
  50. package/templates/frontend/nextjs/next.config.js +4 -0
  51. package/templates/frontend/nextjs/package-lock.json +9631 -0
  52. package/templates/frontend/nextjs/package.json +31 -0
  53. package/templates/frontend/nextjs/start.sh +2 -0
  54. package/templates/frontend/nextjs/test.sh +2 -0
  55. package/templates/frontend/nextjs/tsconfig.json +27 -0
  56. package/templates/frontend/svelte/.eslintrc.cjs +27 -0
  57. package/templates/frontend/svelte/index.html +12 -0
  58. package/templates/frontend/svelte/lint.sh +3 -0
  59. package/templates/frontend/svelte/package-lock.json +6129 -0
  60. package/templates/frontend/svelte/package.json +30 -0
  61. package/templates/frontend/svelte/src/App.spec.ts +36 -0
  62. package/templates/frontend/svelte/src/App.svelte +61 -0
  63. package/templates/frontend/svelte/src/main.ts +8 -0
  64. package/templates/frontend/svelte/src/setupTests.ts +1 -0
  65. package/templates/frontend/svelte/src/svelte-env.d.ts +2 -0
  66. package/templates/frontend/svelte/start.sh +2 -0
  67. package/templates/frontend/svelte/svelte.config.js +5 -0
  68. package/templates/frontend/svelte/test.sh +2 -0
  69. package/templates/frontend/svelte/tsconfig.json +9 -0
  70. package/templates/frontend/svelte/vite.config.ts +15 -0
  71. package/templates/frontend/vue/.eslintrc.cjs +18 -0
  72. package/templates/frontend/vue/index.html +12 -0
  73. package/templates/frontend/vue/lint.sh +3 -0
  74. package/templates/frontend/vue/package-lock.json +5110 -0
  75. package/templates/frontend/vue/package.json +30 -0
  76. package/templates/frontend/vue/src/App.vue +61 -0
  77. package/templates/frontend/vue/src/__tests__/App.spec.ts +37 -0
  78. package/templates/frontend/vue/src/main.ts +4 -0
  79. package/templates/frontend/vue/src/vite-env.d.ts +7 -0
  80. package/templates/frontend/vue/start.sh +2 -0
  81. package/templates/frontend/vue/test.sh +2 -0
  82. package/templates/frontend/vue/tsconfig.json +12 -0
  83. package/templates/frontend/vue/vite.config.ts +14 -0
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "frontend-angular",
3
+ "version": "0.0.0",
4
+ "scripts": {
5
+ "ng": "ng",
6
+ "dev": "ng serve --host 127.0.0.1",
7
+ "build": "ng build",
8
+ "watch": "ng build --watch --configuration development",
9
+ "test": "ng test --watch=false --browsers=ChromeHeadlessCI",
10
+ "lint": "echo 'No lint command configured'"
11
+ },
12
+ "private": true,
13
+ "dependencies": {
14
+ "@angular/animations": "^21.0.0",
15
+ "@angular/common": "^21.0.0",
16
+ "@angular/compiler": "^21.0.0",
17
+ "@angular/core": "^21.0.0",
18
+ "@angular/forms": "^21.0.0",
19
+ "@angular/platform-browser": "^21.0.0",
20
+ "@angular/platform-browser-dynamic": "^21.0.0",
21
+ "@angular/router": "^21.0.0",
22
+ "rxjs": "~7.8.0",
23
+ "tslib": "^2.3.0",
24
+ "zone.js": "~0.15.0"
25
+ },
26
+ "devDependencies": {
27
+ "@angular-devkit/build-angular": "^21.0.0",
28
+ "@angular/cli": "^21.0.0",
29
+ "@angular/compiler-cli": "^21.0.0",
30
+ "@types/jasmine": "~5.1.0",
31
+ "jasmine-core": "~5.1.0",
32
+ "karma": "~6.4.0",
33
+ "karma-chrome-launcher": "~3.2.0",
34
+ "karma-coverage": "~2.2.0",
35
+ "karma-jasmine": "~5.1.0",
36
+ "karma-jasmine-html-reporter": "~2.1.0",
37
+ "puppeteer": "^22.0.0",
38
+ "typescript": "~5.9.0"
39
+ }
40
+ }
@@ -0,0 +1,24 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { AppComponent } from './app.component';
3
+ import { FormsModule } from '@angular/forms';
4
+
5
+ describe('AppComponent', () => {
6
+ beforeEach(async () => {
7
+ await TestBed.configureTestingModule({
8
+ imports: [AppComponent, FormsModule],
9
+ }).compileComponents();
10
+ });
11
+
12
+ it('should create the app', () => {
13
+ const fixture = TestBed.createComponent(AppComponent);
14
+ const app = fixture.componentInstance;
15
+ expect(app).toBeTruthy();
16
+ });
17
+
18
+ it(`should have as title 'Multiplier App (Angular)'`, () => {
19
+ const fixture = TestBed.createComponent(AppComponent);
20
+ fixture.detectChanges();
21
+ const compiled = fixture.nativeElement as HTMLElement;
22
+ expect(compiled.querySelector('h1')?.textContent).toContain('Multiplier App (Angular)');
23
+ });
24
+ });
@@ -0,0 +1,73 @@
1
+ import { Component, ChangeDetectorRef } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormsModule } from '@angular/forms';
4
+
5
+ @Component({
6
+ selector: 'app-root',
7
+ standalone: true,
8
+ imports: [CommonModule, FormsModule],
9
+ template: `
10
+ <main style="padding: 2rem; font-family: sans-serif;">
11
+ <h1>Multiplier App (Angular)</h1>
12
+ <form (submit)="handleSubmit($event)" style="margin-bottom: 1rem;">
13
+ <label for="numberInput" style="display: block; margin-bottom: 0.5rem;">
14
+ Enter a number:
15
+ </label>
16
+ <input
17
+ id="numberInput"
18
+ type="number"
19
+ step="any"
20
+ [(ngModel)]="number"
21
+ name="number"
22
+ required
23
+ style="padding: 0.5rem; margin-right: 0.5rem;"
24
+ />
25
+ <button type="submit" style="padding: 0.5rem 1rem;">
26
+ Multiply by 2
27
+ </button>
28
+ </form>
29
+
30
+ <div *ngIf="result !== null" style="margin-top: 1rem; padding: 1rem; background-color: #e0ffe0; border: 1px solid #00cc00;">
31
+ <strong>Result:</strong> {{ result }}
32
+ </div>
33
+
34
+ <div *ngIf="error" style="margin-top: 1rem; padding: 1rem; background-color: #ffe0e0; border: 1px solid #cc0000;">
35
+ <strong>Error:</strong> {{ error }}
36
+ </div>
37
+ </main>
38
+ `,
39
+ })
40
+ export class AppComponent {
41
+ number: string = '';
42
+ result: number | null = null;
43
+ error: string | null = null;
44
+
45
+ constructor(private cdr: ChangeDetectorRef) {}
46
+
47
+ async handleSubmit(event: Event) {
48
+ event.preventDefault();
49
+ this.error = null;
50
+ this.result = null;
51
+
52
+ try {
53
+ const response = await fetch('http://localhost:8000/api/multiply', {
54
+ method: 'POST',
55
+ headers: {
56
+ 'Content-Type': 'application/json',
57
+ },
58
+ body: JSON.stringify({ number: parseFloat(this.number) }),
59
+ });
60
+
61
+ if (!response.ok) {
62
+ throw new Error('Network response was not ok');
63
+ }
64
+
65
+ const data = await response.json();
66
+ this.result = data.result;
67
+ } catch (err: unknown) {
68
+ this.error = (err as Error).message;
69
+ }
70
+
71
+ this.cdr.detectChanges();
72
+ }
73
+ }
File without changes
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Angular App</title>
6
+ <base href="/">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1">
8
+ </head>
9
+ <body>
10
+ <app-root></app-root>
11
+ </body>
12
+ </html>
@@ -0,0 +1,5 @@
1
+ import { bootstrapApplication } from '@angular/platform-browser';
2
+ import { AppComponent } from './app/app.component';
3
+
4
+ bootstrapApplication(AppComponent)
5
+ .catch((err) => console.error(err));
@@ -0,0 +1 @@
1
+ /* You can add global styles to this file, and also import other style files */
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bash
2
+ npm run dev
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bash
2
+ npm run test
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist/out-tsc",
5
+ "types": []
6
+ },
7
+ "files": [
8
+ "src/main.ts"
9
+ ],
10
+ "include": [
11
+ "src/**/*.ts"
12
+ ],
13
+ "exclude": [
14
+ "src/**/*.spec.ts"
15
+ ]
16
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "compileOnSave": false,
3
+ "compilerOptions": {
4
+ "outDir": "./dist/out-tsc",
5
+ "forceConsistentCasingInFileNames": true,
6
+ "strict": true,
7
+ "noImplicitOverride": true,
8
+ "noPropertyAccessFromIndexSignature": true,
9
+ "noImplicitReturns": true,
10
+ "noFallthroughCasesInSwitch": true,
11
+ "skipLibCheck": true,
12
+ "esModuleInterop": true,
13
+ "sourceMap": true,
14
+ "declaration": false,
15
+ "experimentalDecorators": true,
16
+ "moduleResolution": "bundler",
17
+ "importHelpers": true,
18
+ "target": "ES2022",
19
+ "module": "ES2022",
20
+ "useDefineForClassFields": false,
21
+ "lib": [
22
+ "ES2022",
23
+ "dom"
24
+ ]
25
+ },
26
+ "angularCompilerOptions": {
27
+ "enableI18nLegacyMessageIdFormat": false,
28
+ "strictInjectionParameters": true,
29
+ "strictInputAccessModifiers": true,
30
+ "strictTemplates": true
31
+ }
32
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist/out-tsc",
5
+ "types": [
6
+ "jasmine"
7
+ ]
8
+ },
9
+ "include": [
10
+ "src/**/*.spec.ts",
11
+ "src/**/*.ts",
12
+ "src/**/*.d.ts"
13
+ ]
14
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "next/core-web-vitals"
3
+ }
@@ -0,0 +1,39 @@
1
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
2
+ import Home from '../app/page';
3
+
4
+ // Mock fetch
5
+ global.fetch = jest.fn(() =>
6
+ Promise.resolve({
7
+ ok: true,
8
+ json: () => Promise.resolve({ result: 10 }),
9
+ })
10
+ );
11
+
12
+ describe('Home', () => {
13
+ beforeEach(() => {
14
+ fetch.mockClear();
15
+ });
16
+
17
+ it('renders a heading', () => {
18
+ render(<Home />);
19
+ const heading = screen.getByRole('heading', { name: /Multiplier App/i });
20
+ expect(heading).toBeInTheDocument();
21
+ });
22
+
23
+ it('submits a number and displays the result', async () => {
24
+ render(<Home />);
25
+
26
+ const input = screen.getByLabelText(/Enter a number/i);
27
+ const button = screen.getByRole('button', { name: /Multiply by 2/i });
28
+
29
+ fireEvent.change(input, { target: { value: '5' } });
30
+ fireEvent.click(button);
31
+
32
+ expect(fetch).toHaveBeenCalledTimes(1);
33
+ expect(fetch).toHaveBeenCalledWith('http://localhost:8000/api/multiply', expect.any(Object));
34
+
35
+ const result = await waitFor(() => screen.getByText(/Result:/i));
36
+ expect(result).toBeInTheDocument();
37
+ expect(screen.getByText('10')).toBeInTheDocument();
38
+ });
39
+ });
@@ -0,0 +1,18 @@
1
+ import type { Metadata } from 'next'
2
+
3
+ export const metadata: Metadata = {
4
+ title: 'Fullstack App',
5
+ description: 'A simple fullstack application',
6
+ }
7
+
8
+ export default function RootLayout({
9
+ children,
10
+ }: {
11
+ children: React.ReactNode
12
+ }) {
13
+ return (
14
+ <html lang="en">
15
+ <body>{children}</body>
16
+ </html>
17
+ )
18
+ }
@@ -0,0 +1,69 @@
1
+ 'use client';
2
+
3
+ import { useState, FormEvent } from 'react';
4
+
5
+ export default function Home() {
6
+ const [number, setNumber] = useState<string>('');
7
+ const [result, setResult] = useState<number | null>(null);
8
+ const [error, setError] = useState<string | null>(null);
9
+
10
+ const handleSubmit = async (e: FormEvent) => {
11
+ e.preventDefault();
12
+ setError(null);
13
+ setResult(null);
14
+
15
+ try {
16
+ const response = await fetch('http://localhost:8000/api/multiply', {
17
+ method: 'POST',
18
+ headers: {
19
+ 'Content-Type': 'application/json',
20
+ },
21
+ body: JSON.stringify({ number: parseFloat(number) }),
22
+ });
23
+
24
+ if (!response.ok) {
25
+ throw new Error('Network response was not ok');
26
+ }
27
+
28
+ const data = await response.json();
29
+ setResult(data.result);
30
+ } catch (err: unknown) {
31
+ setError((err as Error).message);
32
+ }
33
+ };
34
+
35
+ return (
36
+ <main style={{ padding: '2rem', fontFamily: 'sans-serif' }}>
37
+ <h1>Multiplier App</h1>
38
+ <form onSubmit={handleSubmit} style={{ marginBottom: '1rem' }}>
39
+ <label htmlFor="numberInput" style={{ display: 'block', marginBottom: '0.5rem' }}>
40
+ Enter a number:
41
+ </label>
42
+ <input
43
+ id="numberInput"
44
+ type="number"
45
+ step="any"
46
+ value={number}
47
+ onChange={(e) => setNumber(e.target.value)}
48
+ required
49
+ style={{ padding: '0.5rem', marginRight: '0.5rem' }}
50
+ />
51
+ <button type="submit" style={{ padding: '0.5rem 1rem' }}>
52
+ Multiply by 2
53
+ </button>
54
+ </form>
55
+
56
+ {result !== null && (
57
+ <div style={{ marginTop: '1rem', padding: '1rem', backgroundColor: '#e0ffe0', border: '1px solid #00cc00' }}>
58
+ <strong>Result:</strong> {result}
59
+ </div>
60
+ )}
61
+
62
+ {error && (
63
+ <div style={{ marginTop: '1rem', padding: '1rem', backgroundColor: '#ffe0e0', border: '1px solid #cc0000' }}>
64
+ <strong>Error:</strong> {error}
65
+ </div>
66
+ )}
67
+ </main>
68
+ );
69
+ }
@@ -0,0 +1,16 @@
1
+ const nextJest = require('next/jest')
2
+
3
+ const createJestConfig = nextJest({
4
+ // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
5
+ dir: './',
6
+ })
7
+
8
+ // Add any custom config to be passed to Jest
9
+ const customJestConfig = {
10
+ setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
11
+ testEnvironment: 'jest-environment-jsdom',
12
+ testMatch: ['**/__tests__/**/*.test.[jt]s?(x)'],
13
+ }
14
+
15
+ // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
16
+ module.exports = createJestConfig(customJestConfig)
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+ npm run lint
3
+ npx prettier --write .
@@ -0,0 +1,4 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {};
3
+
4
+ module.exports = nextConfig;