├── .editorconfig ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json └── tasks.json ├── README.md ├── angular.json ├── app.config.ts ├── index.client.html ├── package-lock.json ├── package.json ├── public ├── .gitkeep └── favicon.ico ├── src ├── app │ ├── app-root.analog │ ├── app.config.server.ts │ ├── app.config.ts │ └── pages │ │ ├── get-posts.ts │ │ └── index.page.analog ├── main.server.ts ├── main.ts ├── styles.css └── vite-env.d.ts ├── tsconfig.app.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | /.nx/cache 34 | /.nx/workspace-data 35 | .sass-cache/ 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | testem.log 40 | /typings 41 | 42 | # System files 43 | .DS_Store 44 | Thumbs.db 45 | 46 | .output 47 | .vinxi 48 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template", "analogjs.vscode-analog"] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "ng serve", 7 | "type": "chrome", 8 | "request": "launch", 9 | "preLaunchTask": "npm: start", 10 | "url": "http://localhost:5173/" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "type": "npm", 7 | "script": "start", 8 | "isBackground": true, 9 | "problemMatcher": { 10 | "owner": "typescript", 11 | "pattern": "$tsc", 12 | "background": { 13 | "activeOnStart": true, 14 | "beginsPattern": { 15 | "regexp": "(.*?)" 16 | }, 17 | "endsPattern": { 18 | "regexp": "bundle generation complete" 19 | } 20 | } 21 | } 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular Start: Analog SFCs, Vinxi, and Server Functions 2 | 3 | - [Analog](https://analogjs.org) 4 | - [Angular](https://angular.dev) 5 | - [Vinxi](https://vinxi.vercel.app) 6 | 7 | ## Setup 8 | 9 | Run `npm install` to install the application dependencies. 10 | 11 | ## Development 12 | 13 | Run `npm run dev` for a dev server. Navigate to `http://localhost:3000/`. 14 | 15 | ## Build 16 | 17 | Run `npm run build` to build the client/server project. The build artifacts are located in the `.output` directory. 18 | 19 | ## Community 20 | 21 | - Visit and Star the [GitHub Repo](https://github.com/analogjs/analog) 22 | - Join the [Discord](https://chat.analogjs.org) 23 | - Follow us on [Twitter](https://twitter.com/analogjs) 24 | - Become a [Sponsor](https://github.com/sponsors/brandonroberts) 25 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "my-app": { 7 | "projectType": "application", 8 | "root": ".", 9 | "sourceRoot": "src", 10 | "prefix": "app", 11 | "architect": { 12 | "build": { 13 | "builder": "@analogjs/platform:vite", 14 | "options": { 15 | "configFile": "vite.config.ts", 16 | "main": "src/main.ts", 17 | "outputPath": "dist/client", 18 | "tsConfig": "tsconfig.app.json" 19 | }, 20 | "defaultConfiguration": "production", 21 | "configurations": { 22 | "development": { 23 | "mode": "development" 24 | }, 25 | "production": { 26 | "sourcemap": false, 27 | "mode": "production" 28 | } 29 | } 30 | }, 31 | "serve": { 32 | "builder": "@analogjs/platform:vite-dev-server", 33 | "defaultConfiguration": "development", 34 | "options": { 35 | "buildTarget": "my-app:build", 36 | "port": 5173 37 | }, 38 | "configurations": { 39 | "development": { 40 | "buildTarget": "my-app:build:development", 41 | "hmr": true 42 | }, 43 | "production": { 44 | "buildTarget": "my-app:build:production" 45 | } 46 | } 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app.config.ts: -------------------------------------------------------------------------------- 1 | import angular from '@analogjs/vite-plugin-angular'; 2 | import { createApp } from "vinxi"; 3 | import { serverFunctions } from '@vinxi/server-functions/plugin'; 4 | 5 | export default createApp({ 6 | routers: [ 7 | { 8 | name: "public", 9 | type: "static", 10 | dir: "./public", 11 | }, 12 | { 13 | name: "ssr", 14 | type: "http", 15 | handler: "./src/main.server.ts", 16 | plugins: () => [ 17 | { 18 | config() { 19 | return { 20 | ssr: { 21 | noExternal: [ 22 | '@angular/**', 23 | '@analogjs/**', 24 | 'zone.js/**' 25 | ] 26 | } 27 | } 28 | }, 29 | }, 30 | angular({ 31 | experimental: { 32 | supportAnalogFormat: true 33 | } 34 | }), 35 | ], 36 | }, 37 | { 38 | name: "client", 39 | type: "client", 40 | handler: "./src/main.ts", 41 | base: "/_base", 42 | plugins: () => [ 43 | angular({ 44 | transformFilter(code) { 45 | return !code.includes('createServerReference'); 46 | }, 47 | experimental: { 48 | supportAnalogFormat: true 49 | } 50 | }), 51 | serverFunctions.client() 52 | ], 53 | }, 54 | serverFunctions.router() 55 | ], 56 | }); -------------------------------------------------------------------------------- /index.client.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular Start 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "analog-angular-vinxi", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "engines": { 6 | "node": ">=18.19.1" 7 | }, 8 | "scripts": { 9 | "ng": "ng", 10 | "dev": "vinxi dev", 11 | "start": "npm run dev", 12 | "build": "vinxi build", 13 | "watch": "ng build --watch --configuration development", 14 | "test": "ng test" 15 | }, 16 | "private": true, 17 | "dependencies": { 18 | "@analogjs/content": "^1.7.3-beta.1", 19 | "@analogjs/router": "^1.7.3-beta.1", 20 | "@angular/animations": "^18.0.0", 21 | "@angular/build": "^18.0.0", 22 | "@angular/common": "^18.0.0", 23 | "@angular/compiler": "^18.0.0", 24 | "@angular/core": "^18.0.0", 25 | "@angular/forms": "^18.0.0", 26 | "@angular/platform-browser": "^18.0.0", 27 | "@angular/platform-browser-dynamic": "^18.0.0", 28 | "@angular/platform-server": "^18.0.0", 29 | "@angular/router": "^18.0.0", 30 | "front-matter": "^4.0.2", 31 | "marked": "^5.0.2", 32 | "marked-gfm-heading-id": "^3.1.0", 33 | "marked-highlight": "^2.0.1", 34 | "marked-mangle": "^1.1.7", 35 | "prismjs": "^1.29.0", 36 | "rxjs": "~7.8.0", 37 | "tslib": "^2.3.0", 38 | "zone.js": "~0.14.3" 39 | }, 40 | "devDependencies": { 41 | "@analogjs/platform": "^1.7.3-beta.1", 42 | "@analogjs/vite-plugin-angular": "^1.7.3-beta.1", 43 | "@analogjs/vitest-angular": "^1.7.3-beta.1", 44 | "@angular/cli": "^18.0.0", 45 | "@angular/compiler-cli": "^18.0.0", 46 | "@vinxi/server-functions": "^0.4.2", 47 | "typescript": "~5.4.2", 48 | "vinxi": "^0.4.2", 49 | "vite": "^5.0.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /public/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brandonroberts/analog-angular-start/04f633016b5f50c2e17a86c5bd2382d98cddc909/public/.gitkeep -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brandonroberts/analog-angular-start/04f633016b5f50c2e17a86c5bd2382d98cddc909/public/favicon.ico -------------------------------------------------------------------------------- /src/app/app-root.analog: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/app/app.config.server.ts: -------------------------------------------------------------------------------- 1 | import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; 2 | import { provideServerRendering } from '@angular/platform-server'; 3 | 4 | import { appConfig } from './app.config'; 5 | 6 | const serverConfig: ApplicationConfig = { 7 | providers: [provideServerRendering()], 8 | }; 9 | 10 | export const config = mergeApplicationConfig(appConfig, serverConfig); 11 | -------------------------------------------------------------------------------- /src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | provideHttpClient, 3 | withFetch, 4 | } from '@angular/common/http'; 5 | import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; 6 | import { provideClientHydration } from '@angular/platform-browser'; 7 | import { provideRouter } from '@angular/router'; 8 | 9 | export const appConfig: ApplicationConfig = { 10 | providers: [ 11 | provideZoneChangeDetection({ eventCoalescing: true }), 12 | provideRouter([ 13 | { path: '', pathMatch: 'full', loadComponent: () => import('./pages/index.page.analog') }, 14 | { path: '**', loadComponent: () => import('./pages/index.page.analog') } 15 | ]), 16 | provideHttpClient( 17 | withFetch(), 18 | ), 19 | provideClientHydration() 20 | ], 21 | }; 22 | -------------------------------------------------------------------------------- /src/app/pages/get-posts.ts: -------------------------------------------------------------------------------- 1 | 'use server'; 2 | 3 | export async function getPosts() { 4 | const res = await fetch('https://jsonplaceholder.typicode.com/posts'); 5 | 6 | if (!res.ok) { 7 | return { error: true }; 8 | } 9 | 10 | const data = await res.json(); 11 | 12 | console.log('server!'); 13 | 14 | return data; 15 | } -------------------------------------------------------------------------------- /src/app/pages/index.page.analog: -------------------------------------------------------------------------------- 1 | 17 | 18 | 35 | 36 | -------------------------------------------------------------------------------- /src/main.server.ts: -------------------------------------------------------------------------------- 1 | import 'zone.js/node'; 2 | import '@angular/platform-server/init'; 3 | import { enableProdMode } from '@angular/core'; 4 | import { bootstrapApplication } from '@angular/platform-browser'; 5 | import { renderApplication } from '@angular/platform-server'; 6 | import { provideServerContext } from '@analogjs/router/server'; 7 | import { ServerRequest } from '@analogjs/router/tokens'; 8 | import { defineEventHandler } from 'vinxi/http'; 9 | import { getManifest } from 'vinxi/manifest'; 10 | 11 | import template from '../index.client.html?raw'; 12 | 13 | import { config } from './app/app.config.server'; 14 | import App from './app/app-root.analog'; 15 | 16 | if (import.meta.env.PROD) { 17 | enableProdMode(); 18 | } 19 | 20 | export function bootstrap() { 21 | return bootstrapApplication(App, config); 22 | } 23 | 24 | export default defineEventHandler(async(event) => { 25 | const clientManifest = getManifest('client'); 26 | const client = import.meta.env.DEV ? '/_base/src/main.ts': clientManifest.inputs[clientManifest.handler].output.path; 27 | const html = await renderApplication(bootstrap, { 28 | document: template.replace('__MAIN__', client), 29 | url: event.node.req.originalUrl, 30 | platformProviders: [provideServerContext({ req: event.node.req as ServerRequest, res: event.node.res })], 31 | }); 32 | 33 | return html; 34 | }); 35 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import 'zone.js'; 2 | import { bootstrapApplication } from '@angular/platform-browser'; 3 | 4 | import App from './app/app-root.analog'; 5 | import { appConfig } from './app/app.config'; 6 | 7 | bootstrapApplication(App, appConfig); 8 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.analog' { 4 | const cmp: any; 5 | export default cmp; 6 | } -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": ["src/main.ts", "src/main.server.ts"], 9 | "include": [ 10 | "src/**/*.d.ts", 11 | "src/app/pages/**/*.page.ts", 12 | "src/server/middleware/**/*.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "downlevelIteration": true, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "Bundler", 18 | "importHelpers": true, 19 | "target": "ES2022", 20 | "module": "ES2022", 21 | "lib": ["ES2022", "dom"], 22 | "useDefineForClassFields": false, 23 | "skipLibCheck": true 24 | }, 25 | "angularCompilerOptions": { 26 | "enableI18nLegacyMessageIdFormat": false, 27 | "strictInjectionParameters": true, 28 | "strictInputAccessModifiers": true, 29 | "strictTemplates": true 30 | } 31 | } 32 | --------------------------------------------------------------------------------