create-ng-tailwind 2.0.1 → 2.1.1

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,290 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * SEO Features for Starter Template
6
+ * Simple and focused SEO implementation - developers can extend as needed
7
+ */
8
+
9
+ /**
10
+ * Create simplified SEO Service (only essential features)
11
+ */
12
+ function createSEOService() {
13
+ return `import { Injectable, inject } from '@angular/core';
14
+ import { Meta, Title } from '@angular/platform-browser';
15
+ import { Router, NavigationEnd } from '@angular/router';
16
+ import { DOCUMENT } from '@angular/common';
17
+ import { filter } from 'rxjs/operators';
18
+
19
+ /**
20
+ * SEO Service - Simple SEO management for Angular apps
21
+ *
22
+ * Includes:
23
+ * - Dynamic meta tags (title, description, keywords)
24
+ * - Open Graph tags for social sharing (Facebook, LinkedIn)
25
+ * - Twitter Card tags
26
+ * - Canonical URLs (auto-updated on route changes)
27
+ * - Structured data (JSON-LD) support
28
+ *
29
+ * @example
30
+ * // In any component
31
+ * constructor(private seo: SeoService) {}
32
+ *
33
+ * ngOnInit() {
34
+ * this.seo.updateMeta({
35
+ * title: 'About Us',
36
+ * description: 'Learn more about our company',
37
+ * keywords: 'about, company, team'
38
+ * });
39
+ * }
40
+ */
41
+ @Injectable({
42
+ providedIn: 'root'
43
+ })
44
+ export class SeoService {
45
+ private meta = inject(Meta);
46
+ private title = inject(Title);
47
+ private router = inject(Router);
48
+ private document = inject(DOCUMENT);
49
+
50
+ // Default configuration - update these values for your app
51
+ private readonly config = {
52
+ siteName: 'My Angular App', // TODO: Update with your site name
53
+ siteUrl: 'https://example.com', // TODO: Update with your production URL
54
+ titleSuffix: ' | My Angular App',
55
+ defaultDescription: 'A modern Angular application built with best practices and SEO optimization.',
56
+ defaultImage: '/assets/images/og-default.svg', // TODO: Replace with JPG/PNG for better compatibility
57
+ };
58
+
59
+ constructor() {
60
+ // Auto-update canonical URL on route changes
61
+ this.router.events
62
+ .pipe(filter((event) => event instanceof NavigationEnd))
63
+ .subscribe(() => {
64
+ this.updateCanonicalUrl();
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Update SEO meta tags for a page
70
+ */
71
+ updateMeta(options: {
72
+ title?: string;
73
+ description?: string;
74
+ keywords?: string;
75
+ ogImage?: string;
76
+ ogType?: 'website' | 'article';
77
+ }): void {
78
+ // Update title
79
+ if (options.title) {
80
+ const fullTitle = options.title + this.config.titleSuffix;
81
+ this.title.setTitle(fullTitle);
82
+ }
83
+
84
+ // Update meta tags
85
+ const description = options.description || this.config.defaultDescription;
86
+ const image = this.getFullUrl(options.ogImage || this.config.defaultImage);
87
+
88
+ this.meta.updateTag({ name: 'description', content: description });
89
+ if (options.keywords) {
90
+ this.meta.updateTag({ name: 'keywords', content: options.keywords });
91
+ }
92
+
93
+ // Open Graph tags
94
+ this.meta.updateTag({ property: 'og:title', content: options.title || this.config.siteName });
95
+ this.meta.updateTag({ property: 'og:description', content: description });
96
+ this.meta.updateTag({ property: 'og:image', content: image });
97
+ this.meta.updateTag({ property: 'og:url', content: this.getCurrentUrl() });
98
+ this.meta.updateTag({ property: 'og:type', content: options.ogType || 'website' });
99
+ this.meta.updateTag({ property: 'og:site_name', content: this.config.siteName });
100
+
101
+ // Twitter Card tags
102
+ this.meta.updateTag({ property: 'twitter:card', content: 'summary_large_image' });
103
+ this.meta.updateTag({ property: 'twitter:title', content: options.title || this.config.siteName });
104
+ this.meta.updateTag({ property: 'twitter:description', content: description });
105
+ this.meta.updateTag({ property: 'twitter:image', content: image });
106
+
107
+ // Update canonical URL
108
+ this.updateCanonicalUrl();
109
+ }
110
+
111
+ /**
112
+ * Add structured data (JSON-LD) to the page
113
+ * Use with helper functions from @core/utils/structured-data
114
+ */
115
+ addStructuredData(data: Record<string, unknown>): void {
116
+ let script: HTMLScriptElement | null = this.document.querySelector('script[type="application/ld+json"]');
117
+
118
+ if (!script) {
119
+ script = this.document.createElement('script');
120
+ script.type = 'application/ld+json';
121
+ this.document.head.appendChild(script);
122
+ }
123
+
124
+ script.textContent = JSON.stringify(data);
125
+ }
126
+
127
+ /**
128
+ * Update canonical URL based on current route
129
+ */
130
+ private updateCanonicalUrl(): void {
131
+ const currentUrl = this.getCurrentUrl();
132
+ let link: HTMLLinkElement | null = this.document.querySelector('link[rel="canonical"]');
133
+
134
+ if (!link) {
135
+ link = this.document.createElement('link');
136
+ link.setAttribute('rel', 'canonical');
137
+ this.document.head.appendChild(link);
138
+ }
139
+
140
+ link.setAttribute('href', currentUrl);
141
+ }
142
+
143
+ /**
144
+ * Get current full URL
145
+ */
146
+ private getCurrentUrl(): string {
147
+ return this.config.siteUrl + this.router.url;
148
+ }
149
+
150
+ /**
151
+ * Convert relative URL to absolute URL
152
+ */
153
+ private getFullUrl(url: string): string {
154
+ if (url.startsWith('http://') || url.startsWith('https://')) {
155
+ return url;
156
+ }
157
+ return this.config.siteUrl + url;
158
+ }
159
+ }
160
+ `;
161
+ }
162
+
163
+ /**
164
+ * Create simplified Structured Data Utility (only essential schemas)
165
+ */
166
+ function createStructuredDataUtil() {
167
+ return `/**
168
+ * Structured Data Utility - Simple Schema.org helpers
169
+ *
170
+ * Provides basic schemas for SEO. Add more schemas as needed.
171
+ *
172
+ * @see https://schema.org
173
+ * @see https://developers.google.com/search/docs/appearance/structured-data
174
+ */
175
+
176
+ export interface Organization {
177
+ name: string;
178
+ url: string;
179
+ logo: string;
180
+ description?: string;
181
+ sameAs?: string[]; // Social media profiles
182
+ }
183
+
184
+ export interface WebSite {
185
+ name: string;
186
+ url: string;
187
+ description?: string;
188
+ }
189
+
190
+ /**
191
+ * Generate Organization structured data
192
+ *
193
+ * @example
194
+ * const orgData = createOrganizationSchema({
195
+ * name: 'My Company',
196
+ * url: 'https://example.com',
197
+ * logo: '/assets/images/logo.svg',
198
+ * sameAs: ['https://twitter.com/mycompany']
199
+ * });
200
+ */
201
+ export function createOrganizationSchema(org: Organization): Record<string, unknown> {
202
+ const schema: Record<string, unknown> = {
203
+ '@context': 'https://schema.org',
204
+ '@type': 'Organization',
205
+ name: org.name,
206
+ url: org.url,
207
+ logo: org.logo,
208
+ };
209
+
210
+ if (org.description) {
211
+ schema['description'] = org.description;
212
+ }
213
+
214
+ if (org.sameAs && org.sameAs.length > 0) {
215
+ schema['sameAs'] = org.sameAs;
216
+ }
217
+
218
+ return schema;
219
+ }
220
+
221
+ /**
222
+ * Generate WebSite structured data
223
+ *
224
+ * @example
225
+ * const siteData = createWebSiteSchema({
226
+ * name: 'My Website',
227
+ * url: 'https://example.com',
228
+ * description: 'A modern web application'
229
+ * });
230
+ */
231
+ export function createWebSiteSchema(site: WebSite): Record<string, unknown> {
232
+ const schema: Record<string, unknown> = {
233
+ '@context': 'https://schema.org',
234
+ '@type': 'WebSite',
235
+ name: site.name,
236
+ url: site.url,
237
+ };
238
+
239
+ if (site.description) {
240
+ schema['description'] = site.description;
241
+ }
242
+
243
+ return schema;
244
+ }
245
+
246
+ /**
247
+ * NEED MORE SCHEMAS?
248
+ *
249
+ * You can easily add more schemas as needed:
250
+ * - Article - for blog posts
251
+ * - Product - for e-commerce
252
+ * - BreadcrumbList - for navigation
253
+ * - FAQ - for FAQ pages
254
+ * - Person - for author profiles
255
+ *
256
+ * See: https://schema.org/docs/full.html
257
+ */
258
+ `;
259
+ }
260
+
261
+ /**
262
+ * Create robots.txt for Angular apps
263
+ */
264
+ function createRobotsTxt() {
265
+ return `# robots.txt for Angular Application
266
+ # Allow all search engines to crawl the site
267
+
268
+ User-agent: *
269
+ Allow: /
270
+
271
+ # Disallow admin areas (if any)
272
+ # Disallow: /admin/
273
+ # Disallow: /api/
274
+
275
+ # Disallow Angular assets that shouldn't be indexed
276
+ Disallow: /*.js$
277
+ Disallow: /*.css$
278
+ Disallow: /*.json$
279
+
280
+ # Sitemap location
281
+ # TODO: Update this with your actual sitemap URL after deployment
282
+ Sitemap: https://example.com/sitemap.xml
283
+ `;
284
+ }
285
+
286
+ module.exports = {
287
+ createSEOService,
288
+ createStructuredDataUtil,
289
+ createRobotsTxt,
290
+ };