@harpy-js/core 0.5.1 ā 0.5.3
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.
- package/dist/cli.js +3 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -1
- package/dist/seo/index.d.ts +5 -0
- package/dist/seo/index.js +12 -0
- package/dist/seo/robots.controller.d.ts +6 -0
- package/dist/seo/robots.controller.js +37 -0
- package/dist/seo/seo.module.d.ts +7 -0
- package/dist/seo/seo.module.js +54 -0
- package/dist/seo/seo.service.d.ts +16 -0
- package/dist/seo/seo.service.js +136 -0
- package/dist/seo/seo.types.d.ts +29 -0
- package/dist/seo/seo.types.js +2 -0
- package/dist/seo/sitemap.controller.d.ts +6 -0
- package/dist/seo/sitemap.controller.js +37 -0
- package/package.json +1 -1
- package/scripts/auto-wrap-exports.ts +7 -6
- package/scripts/build-hydration.ts +14 -15
- package/scripts/build-page-styles.ts +6 -5
- package/scripts/build.ts +93 -0
- package/scripts/dev.ts +40 -31
- package/scripts/logger.ts +53 -0
- package/scripts/start.ts +41 -0
- package/src/cli.ts +3 -1
- package/src/index.ts +5 -0
- package/src/seo/index.ts +5 -0
- package/src/seo/robots.controller.ts +15 -0
- package/src/seo/seo.module.ts +59 -0
- package/src/seo/seo.service.ts +161 -0
- package/src/seo/seo.types.ts +40 -0
- package/src/seo/sitemap.controller.ts +15 -0
package/dist/cli.js
CHANGED
|
@@ -8,11 +8,13 @@ const scripts = {
|
|
|
8
8
|
"build-hydration": path.join(__dirname, "../scripts/build-hydration.ts"),
|
|
9
9
|
"auto-wrap": path.join(__dirname, "../scripts/auto-wrap-exports.ts"),
|
|
10
10
|
"build-styles": path.join(__dirname, "../scripts/build-page-styles.ts"),
|
|
11
|
+
build: path.join(__dirname, "../scripts/build.ts"),
|
|
12
|
+
start: path.join(__dirname, "../scripts/start.ts"),
|
|
11
13
|
dev: path.join(__dirname, "../scripts/dev.ts"),
|
|
12
14
|
};
|
|
13
15
|
if (!command || !scripts[command]) {
|
|
14
16
|
console.error("Usage: harpy <command>");
|
|
15
|
-
console.error("Commands: build-hydration, auto-wrap, build-styles
|
|
17
|
+
console.error("Commands: build, start, dev, build-hydration, auto-wrap, build-styles");
|
|
16
18
|
process.exit(1);
|
|
17
19
|
}
|
|
18
20
|
const scriptPath = scripts[command];
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export { StaticAssetsController } from "./core/static-assets.controller";
|
|
|
7
7
|
export { JsxRender } from "./decorators/jsx.decorator";
|
|
8
8
|
export { WithLayout } from "./decorators/layout.decorator";
|
|
9
9
|
export type { MetaOptions, RenderOptions } from "./decorators/jsx.decorator";
|
|
10
|
+
export { SeoModule, BaseSeoService, DefaultSeoService } from "./seo";
|
|
11
|
+
export type { SitemapUrl, RobotsConfig, SeoModuleOptions } from "./seo";
|
|
10
12
|
export type { JsxLayout, JsxLayoutProps, PageProps } from "./types/jsx.types";
|
|
11
13
|
export { RouterModule } from "./core/router.module";
|
|
12
14
|
export { NavigationService } from "./core/navigation.service";
|
|
@@ -17,3 +19,4 @@ export { configureHarpyApp, HarpyAppOptions } from "./core/app-setup";
|
|
|
17
19
|
export { setupHarpyApp } from "./core/app-setup";
|
|
18
20
|
export { default as Link } from "./client/Link";
|
|
19
21
|
export { buildHrefIndex, getActiveItemIdFromIndex, getActiveItemIdFromManifest, } from "./client/getActiveItemId";
|
|
22
|
+
export { useI18n } from "./client/use-i18n";
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getActiveItemIdFromManifest = exports.getActiveItemIdFromIndex = exports.buildHrefIndex = exports.Link = exports.setupHarpyApp = exports.configureHarpyApp = exports.AutoRegisterModule = exports.NavigationService = exports.RouterModule = exports.WithLayout = exports.JsxRender = exports.StaticAssetsController = exports.LiveReloadController = exports.withJsxEngine = exports.getHydrationManifest = exports.getChunkPath = exports.initializeHydrationContext = exports.hydrationContext = exports.autoWrapClientComponent = void 0;
|
|
6
|
+
exports.useI18n = exports.getActiveItemIdFromManifest = exports.getActiveItemIdFromIndex = exports.buildHrefIndex = exports.Link = exports.setupHarpyApp = exports.configureHarpyApp = exports.AutoRegisterModule = exports.NavigationService = exports.RouterModule = exports.DefaultSeoService = exports.BaseSeoService = exports.SeoModule = exports.WithLayout = exports.JsxRender = exports.StaticAssetsController = exports.LiveReloadController = exports.withJsxEngine = exports.getHydrationManifest = exports.getChunkPath = exports.initializeHydrationContext = exports.hydrationContext = exports.autoWrapClientComponent = void 0;
|
|
7
7
|
var client_component_wrapper_1 = require("./core/client-component-wrapper");
|
|
8
8
|
Object.defineProperty(exports, "autoWrapClientComponent", { enumerable: true, get: function () { return client_component_wrapper_1.autoWrapClientComponent; } });
|
|
9
9
|
var hydration_1 = require("./core/hydration");
|
|
@@ -22,6 +22,10 @@ var jsx_decorator_1 = require("./decorators/jsx.decorator");
|
|
|
22
22
|
Object.defineProperty(exports, "JsxRender", { enumerable: true, get: function () { return jsx_decorator_1.JsxRender; } });
|
|
23
23
|
var layout_decorator_1 = require("./decorators/layout.decorator");
|
|
24
24
|
Object.defineProperty(exports, "WithLayout", { enumerable: true, get: function () { return layout_decorator_1.WithLayout; } });
|
|
25
|
+
var seo_1 = require("./seo");
|
|
26
|
+
Object.defineProperty(exports, "SeoModule", { enumerable: true, get: function () { return seo_1.SeoModule; } });
|
|
27
|
+
Object.defineProperty(exports, "BaseSeoService", { enumerable: true, get: function () { return seo_1.BaseSeoService; } });
|
|
28
|
+
Object.defineProperty(exports, "DefaultSeoService", { enumerable: true, get: function () { return seo_1.DefaultSeoService; } });
|
|
25
29
|
var router_module_1 = require("./core/router.module");
|
|
26
30
|
Object.defineProperty(exports, "RouterModule", { enumerable: true, get: function () { return router_module_1.RouterModule; } });
|
|
27
31
|
var navigation_service_1 = require("./core/navigation.service");
|
|
@@ -38,3 +42,5 @@ var getActiveItemId_1 = require("./client/getActiveItemId");
|
|
|
38
42
|
Object.defineProperty(exports, "buildHrefIndex", { enumerable: true, get: function () { return getActiveItemId_1.buildHrefIndex; } });
|
|
39
43
|
Object.defineProperty(exports, "getActiveItemIdFromIndex", { enumerable: true, get: function () { return getActiveItemId_1.getActiveItemIdFromIndex; } });
|
|
40
44
|
Object.defineProperty(exports, "getActiveItemIdFromManifest", { enumerable: true, get: function () { return getActiveItemId_1.getActiveItemIdFromManifest; } });
|
|
45
|
+
var use_i18n_1 = require("./client/use-i18n");
|
|
46
|
+
Object.defineProperty(exports, "useI18n", { enumerable: true, get: function () { return use_i18n_1.useI18n; } });
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { SeoModule } from './seo.module';
|
|
2
|
+
export { BaseSeoService, DefaultSeoService } from './seo.service';
|
|
3
|
+
export { RobotsController } from './robots.controller';
|
|
4
|
+
export { SitemapController } from './sitemap.controller';
|
|
5
|
+
export type { SitemapUrl, RobotsConfig, SeoModuleOptions } from './seo.types';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SitemapController = exports.RobotsController = exports.DefaultSeoService = exports.BaseSeoService = exports.SeoModule = void 0;
|
|
4
|
+
var seo_module_1 = require("./seo.module");
|
|
5
|
+
Object.defineProperty(exports, "SeoModule", { enumerable: true, get: function () { return seo_module_1.SeoModule; } });
|
|
6
|
+
var seo_service_1 = require("./seo.service");
|
|
7
|
+
Object.defineProperty(exports, "BaseSeoService", { enumerable: true, get: function () { return seo_service_1.BaseSeoService; } });
|
|
8
|
+
Object.defineProperty(exports, "DefaultSeoService", { enumerable: true, get: function () { return seo_service_1.DefaultSeoService; } });
|
|
9
|
+
var robots_controller_1 = require("./robots.controller");
|
|
10
|
+
Object.defineProperty(exports, "RobotsController", { enumerable: true, get: function () { return robots_controller_1.RobotsController; } });
|
|
11
|
+
var sitemap_controller_1 = require("./sitemap.controller");
|
|
12
|
+
Object.defineProperty(exports, "SitemapController", { enumerable: true, get: function () { return sitemap_controller_1.SitemapController; } });
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.RobotsController = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const seo_service_1 = require("./seo.service");
|
|
15
|
+
let RobotsController = class RobotsController {
|
|
16
|
+
seoService;
|
|
17
|
+
constructor(seoService) {
|
|
18
|
+
this.seoService = seoService;
|
|
19
|
+
}
|
|
20
|
+
getRobots() {
|
|
21
|
+
const config = this.seoService.getRobotsConfig();
|
|
22
|
+
return this.seoService.formatRobotsTxt(config);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
exports.RobotsController = RobotsController;
|
|
26
|
+
__decorate([
|
|
27
|
+
(0, common_1.Get)(),
|
|
28
|
+
(0, common_1.Header)('Content-Type', 'text/plain'),
|
|
29
|
+
(0, common_1.Header)('Cache-Control', 'public, max-age=86400'),
|
|
30
|
+
__metadata("design:type", Function),
|
|
31
|
+
__metadata("design:paramtypes", []),
|
|
32
|
+
__metadata("design:returntype", String)
|
|
33
|
+
], RobotsController.prototype, "getRobots", null);
|
|
34
|
+
exports.RobotsController = RobotsController = __decorate([
|
|
35
|
+
(0, common_1.Controller)('robots.txt'),
|
|
36
|
+
__metadata("design:paramtypes", [seo_service_1.BaseSeoService])
|
|
37
|
+
], RobotsController);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { DynamicModule, Type } from '@nestjs/common';
|
|
2
|
+
import { BaseSeoService } from './seo.service';
|
|
3
|
+
import type { SeoModuleOptions } from './seo.types';
|
|
4
|
+
export declare class SeoModule {
|
|
5
|
+
static forRoot(options?: SeoModuleOptions): DynamicModule;
|
|
6
|
+
static forRootWithService(customService: Type<BaseSeoService>, options?: SeoModuleOptions): DynamicModule;
|
|
7
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var SeoModule_1;
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.SeoModule = void 0;
|
|
11
|
+
const common_1 = require("@nestjs/common");
|
|
12
|
+
const robots_controller_1 = require("./robots.controller");
|
|
13
|
+
const sitemap_controller_1 = require("./sitemap.controller");
|
|
14
|
+
const seo_service_1 = require("./seo.service");
|
|
15
|
+
let SeoModule = SeoModule_1 = class SeoModule {
|
|
16
|
+
static forRoot(options) {
|
|
17
|
+
return {
|
|
18
|
+
module: SeoModule_1,
|
|
19
|
+
controllers: [robots_controller_1.RobotsController, sitemap_controller_1.SitemapController],
|
|
20
|
+
providers: [
|
|
21
|
+
{
|
|
22
|
+
provide: seo_service_1.SEO_MODULE_OPTIONS,
|
|
23
|
+
useValue: options || {},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
provide: seo_service_1.BaseSeoService,
|
|
27
|
+
useClass: seo_service_1.DefaultSeoService,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
exports: [seo_service_1.BaseSeoService],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
static forRootWithService(customService, options) {
|
|
34
|
+
return {
|
|
35
|
+
module: SeoModule_1,
|
|
36
|
+
controllers: [robots_controller_1.RobotsController, sitemap_controller_1.SitemapController],
|
|
37
|
+
providers: [
|
|
38
|
+
{
|
|
39
|
+
provide: seo_service_1.SEO_MODULE_OPTIONS,
|
|
40
|
+
useValue: options || {},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
provide: seo_service_1.BaseSeoService,
|
|
44
|
+
useClass: customService,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
exports: [seo_service_1.BaseSeoService],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
exports.SeoModule = SeoModule;
|
|
52
|
+
exports.SeoModule = SeoModule = SeoModule_1 = __decorate([
|
|
53
|
+
(0, common_1.Module)({})
|
|
54
|
+
], SeoModule);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SitemapUrl, RobotsConfig, SeoModuleOptions } from './seo.types';
|
|
2
|
+
export declare const SEO_MODULE_OPTIONS = "SEO_MODULE_OPTIONS";
|
|
3
|
+
export declare abstract class BaseSeoService {
|
|
4
|
+
protected readonly options?: SeoModuleOptions | undefined;
|
|
5
|
+
protected readonly baseUrl: string;
|
|
6
|
+
constructor(options?: SeoModuleOptions | undefined);
|
|
7
|
+
abstract getSitemapUrls(): Promise<SitemapUrl[]>;
|
|
8
|
+
abstract getRobotsConfig(): RobotsConfig;
|
|
9
|
+
formatSitemapXml(urls: SitemapUrl[]): string;
|
|
10
|
+
formatRobotsTxt(config: RobotsConfig): string;
|
|
11
|
+
protected escapeXml(str: string): string;
|
|
12
|
+
}
|
|
13
|
+
export declare class DefaultSeoService extends BaseSeoService {
|
|
14
|
+
getSitemapUrls(): Promise<SitemapUrl[]>;
|
|
15
|
+
getRobotsConfig(): RobotsConfig;
|
|
16
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.DefaultSeoService = exports.BaseSeoService = exports.SEO_MODULE_OPTIONS = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
exports.SEO_MODULE_OPTIONS = 'SEO_MODULE_OPTIONS';
|
|
18
|
+
let BaseSeoService = class BaseSeoService {
|
|
19
|
+
options;
|
|
20
|
+
baseUrl;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.options = options;
|
|
23
|
+
this.baseUrl = options?.baseUrl || 'http://localhost:3000';
|
|
24
|
+
}
|
|
25
|
+
formatSitemapXml(urls) {
|
|
26
|
+
const urlEntries = urls
|
|
27
|
+
.map((entry) => {
|
|
28
|
+
const lastmod = entry.lastModified
|
|
29
|
+
? new Date(entry.lastModified).toISOString()
|
|
30
|
+
: new Date().toISOString();
|
|
31
|
+
return ` <url>
|
|
32
|
+
<loc>${this.escapeXml(entry.url)}</loc>
|
|
33
|
+
<lastmod>${lastmod}</lastmod>
|
|
34
|
+
${entry.changeFrequency ? `<changefreq>${entry.changeFrequency}</changefreq>` : ''}
|
|
35
|
+
${entry.priority !== undefined ? `<priority>${entry.priority}</priority>` : ''}
|
|
36
|
+
</url>`;
|
|
37
|
+
})
|
|
38
|
+
.join('\n');
|
|
39
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
40
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
41
|
+
${urlEntries}
|
|
42
|
+
</urlset>`;
|
|
43
|
+
}
|
|
44
|
+
formatRobotsTxt(config) {
|
|
45
|
+
const rules = Array.isArray(config.rules) ? config.rules : [config.rules];
|
|
46
|
+
const rulesText = rules
|
|
47
|
+
.map((rule) => {
|
|
48
|
+
const userAgents = Array.isArray(rule.userAgent)
|
|
49
|
+
? rule.userAgent
|
|
50
|
+
: [rule.userAgent];
|
|
51
|
+
const allows = rule.allow
|
|
52
|
+
? Array.isArray(rule.allow)
|
|
53
|
+
? rule.allow
|
|
54
|
+
: [rule.allow]
|
|
55
|
+
: [];
|
|
56
|
+
const disallows = rule.disallow
|
|
57
|
+
? Array.isArray(rule.disallow)
|
|
58
|
+
? rule.disallow
|
|
59
|
+
: [rule.disallow]
|
|
60
|
+
: [];
|
|
61
|
+
let text = userAgents.map((ua) => `User-agent: ${ua}`).join('\n');
|
|
62
|
+
if (allows.length > 0) {
|
|
63
|
+
text += '\n' + allows.map((path) => `Allow: ${path}`).join('\n');
|
|
64
|
+
}
|
|
65
|
+
if (disallows.length > 0) {
|
|
66
|
+
text +=
|
|
67
|
+
'\n' + disallows.map((path) => `Disallow: ${path}`).join('\n');
|
|
68
|
+
}
|
|
69
|
+
if (rule.crawlDelay) {
|
|
70
|
+
text += `\nCrawl-delay: ${rule.crawlDelay}`;
|
|
71
|
+
}
|
|
72
|
+
return text;
|
|
73
|
+
})
|
|
74
|
+
.join('\n\n');
|
|
75
|
+
let result = rulesText;
|
|
76
|
+
if (config.sitemap) {
|
|
77
|
+
const sitemaps = Array.isArray(config.sitemap)
|
|
78
|
+
? config.sitemap
|
|
79
|
+
: [config.sitemap];
|
|
80
|
+
result += '\n\n' + sitemaps.map((s) => `Sitemap: ${s}`).join('\n');
|
|
81
|
+
}
|
|
82
|
+
if (config.host) {
|
|
83
|
+
result += `\n\nHost: ${config.host}`;
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
escapeXml(str) {
|
|
88
|
+
return str
|
|
89
|
+
.replace(/&/g, '&')
|
|
90
|
+
.replace(/</g, '<')
|
|
91
|
+
.replace(/>/g, '>')
|
|
92
|
+
.replace(/"/g, '"')
|
|
93
|
+
.replace(/'/g, ''');
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
exports.BaseSeoService = BaseSeoService;
|
|
97
|
+
exports.BaseSeoService = BaseSeoService = __decorate([
|
|
98
|
+
(0, common_1.Injectable)(),
|
|
99
|
+
__param(0, (0, common_1.Optional)()),
|
|
100
|
+
__param(0, (0, common_1.Inject)(exports.SEO_MODULE_OPTIONS)),
|
|
101
|
+
__metadata("design:paramtypes", [Object])
|
|
102
|
+
], BaseSeoService);
|
|
103
|
+
let DefaultSeoService = class DefaultSeoService extends BaseSeoService {
|
|
104
|
+
async getSitemapUrls() {
|
|
105
|
+
const now = new Date();
|
|
106
|
+
return [
|
|
107
|
+
{
|
|
108
|
+
url: this.baseUrl,
|
|
109
|
+
lastModified: now,
|
|
110
|
+
changeFrequency: 'daily',
|
|
111
|
+
priority: 1.0,
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
getRobotsConfig() {
|
|
116
|
+
const defaultConfig = {
|
|
117
|
+
rules: {
|
|
118
|
+
userAgent: '*',
|
|
119
|
+
allow: '/',
|
|
120
|
+
},
|
|
121
|
+
sitemap: `${this.baseUrl}/sitemap.xml`,
|
|
122
|
+
host: this.baseUrl,
|
|
123
|
+
};
|
|
124
|
+
if (this.options?.robotsConfig) {
|
|
125
|
+
return {
|
|
126
|
+
...defaultConfig,
|
|
127
|
+
...this.options.robotsConfig,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return defaultConfig;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
exports.DefaultSeoService = DefaultSeoService;
|
|
134
|
+
exports.DefaultSeoService = DefaultSeoService = __decorate([
|
|
135
|
+
(0, common_1.Injectable)()
|
|
136
|
+
], DefaultSeoService);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface SitemapUrl {
|
|
2
|
+
url: string;
|
|
3
|
+
lastModified?: Date | string;
|
|
4
|
+
changeFrequency?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
|
|
5
|
+
priority?: number;
|
|
6
|
+
alternates?: {
|
|
7
|
+
languages?: Record<string, string>;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export interface RobotsConfig {
|
|
11
|
+
rules: {
|
|
12
|
+
userAgent: string | string[];
|
|
13
|
+
allow?: string | string[];
|
|
14
|
+
disallow?: string | string[];
|
|
15
|
+
crawlDelay?: number;
|
|
16
|
+
} | Array<{
|
|
17
|
+
userAgent: string | string[];
|
|
18
|
+
allow?: string | string[];
|
|
19
|
+
disallow?: string | string[];
|
|
20
|
+
crawlDelay?: number;
|
|
21
|
+
}>;
|
|
22
|
+
sitemap?: string | string[];
|
|
23
|
+
host?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface SeoModuleOptions {
|
|
26
|
+
baseUrl: string;
|
|
27
|
+
robotsConfig?: Partial<RobotsConfig>;
|
|
28
|
+
sitemapGenerator?: () => Promise<SitemapUrl[]>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.SitemapController = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const seo_service_1 = require("./seo.service");
|
|
15
|
+
let SitemapController = class SitemapController {
|
|
16
|
+
seoService;
|
|
17
|
+
constructor(seoService) {
|
|
18
|
+
this.seoService = seoService;
|
|
19
|
+
}
|
|
20
|
+
async getSitemap() {
|
|
21
|
+
const urls = await this.seoService.getSitemapUrls();
|
|
22
|
+
return this.seoService.formatSitemapXml(urls);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
exports.SitemapController = SitemapController;
|
|
26
|
+
__decorate([
|
|
27
|
+
(0, common_1.Get)(),
|
|
28
|
+
(0, common_1.Header)('Content-Type', 'application/xml'),
|
|
29
|
+
(0, common_1.Header)('Cache-Control', 'public, max-age=3600, s-maxage=3600'),
|
|
30
|
+
__metadata("design:type", Function),
|
|
31
|
+
__metadata("design:paramtypes", []),
|
|
32
|
+
__metadata("design:returntype", Promise)
|
|
33
|
+
], SitemapController.prototype, "getSitemap", null);
|
|
34
|
+
exports.SitemapController = SitemapController = __decorate([
|
|
35
|
+
(0, common_1.Controller)('sitemap.xml'),
|
|
36
|
+
__metadata("design:paramtypes", [seo_service_1.BaseSeoService])
|
|
37
|
+
], SitemapController);
|
package/package.json
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
|
|
12
12
|
import * as fs from "fs";
|
|
13
13
|
import * as path from "path";
|
|
14
|
+
import { Logger } from "./logger";
|
|
15
|
+
|
|
16
|
+
const logger = new Logger("AutoWrapper");
|
|
14
17
|
|
|
15
18
|
const PROJECT_ROOT = process.cwd();
|
|
16
19
|
const SRC_DIR = path.join(PROJECT_ROOT, "src");
|
|
@@ -213,16 +216,16 @@ function transformCompiledFile(
|
|
|
213
216
|
* Main function
|
|
214
217
|
*/
|
|
215
218
|
function main(): void {
|
|
216
|
-
|
|
219
|
+
logger.log("Auto-wrapping client component exports...");
|
|
217
220
|
|
|
218
221
|
const clientComponents = findClientComponentsInSource();
|
|
219
222
|
|
|
220
223
|
if (clientComponents.size === 0) {
|
|
221
|
-
|
|
224
|
+
logger.warn("No client components found");
|
|
222
225
|
return;
|
|
223
226
|
}
|
|
224
227
|
|
|
225
|
-
|
|
228
|
+
logger.log(`Found ${clientComponents.size} client component(s)`);
|
|
226
229
|
|
|
227
230
|
let wrapped = 0;
|
|
228
231
|
for (const [compiledPath, componentName] of clientComponents) {
|
|
@@ -231,9 +234,7 @@ function main(): void {
|
|
|
231
234
|
}
|
|
232
235
|
}
|
|
233
236
|
|
|
234
|
-
|
|
235
|
-
`\n⨠Auto-wrap complete: ${wrapped}/${clientComponents.size} components wrapped\n`,
|
|
236
|
-
);
|
|
237
|
+
logger.log(`Auto-wrap complete: ${wrapped}/${clientComponents.size} components wrapped`);
|
|
237
238
|
}
|
|
238
239
|
|
|
239
240
|
main();
|
|
@@ -13,6 +13,9 @@ import { execSync } from "child_process";
|
|
|
13
13
|
import * as crypto from "crypto";
|
|
14
14
|
import * as fs from "fs";
|
|
15
15
|
import * as path from "path";
|
|
16
|
+
import { Logger } from "./logger";
|
|
17
|
+
|
|
18
|
+
const logger = new Logger("HydrationBuilder");
|
|
16
19
|
|
|
17
20
|
const PROJECT_ROOT = process.cwd();
|
|
18
21
|
const SRC_DIR = path.join(PROJECT_ROOT, "src");
|
|
@@ -181,11 +184,11 @@ function main(): void {
|
|
|
181
184
|
fs.mkdirSync(HYDRATION_ENTRIES_DIR, { recursive: true });
|
|
182
185
|
}
|
|
183
186
|
|
|
184
|
-
|
|
187
|
+
logger.log("Detecting client components...");
|
|
185
188
|
const clientComponents = findClientComponents();
|
|
186
189
|
|
|
187
190
|
if (clientComponents.length === 0) {
|
|
188
|
-
|
|
191
|
+
logger.warn("No client components found");
|
|
189
192
|
// Still ensure chunks directory exists and clear manifest
|
|
190
193
|
if (!fs.existsSync(CHUNKS_DIR)) {
|
|
191
194
|
fs.mkdirSync(CHUNKS_DIR, { recursive: true });
|
|
@@ -207,7 +210,7 @@ function main(): void {
|
|
|
207
210
|
fs.mkdirSync(CHUNKS_DIR, { recursive: true });
|
|
208
211
|
}
|
|
209
212
|
|
|
210
|
-
|
|
213
|
+
logger.log("Generating hydration entries...");
|
|
211
214
|
|
|
212
215
|
// Generate hydration entry files
|
|
213
216
|
const entryFiles: { path: string; componentName: string }[] = [];
|
|
@@ -223,11 +226,10 @@ function main(): void {
|
|
|
223
226
|
path: entryPath,
|
|
224
227
|
componentName: component.componentName,
|
|
225
228
|
});
|
|
226
|
-
console.log(` ā ${component.componentName}.tsx`);
|
|
227
229
|
}
|
|
228
230
|
|
|
229
231
|
// Build shared vendor bundle first
|
|
230
|
-
|
|
232
|
+
logger.log("Building shared vendor bundle...");
|
|
231
233
|
const vendorEntryPath = path.join(HYDRATION_ENTRIES_DIR, "_vendor.js");
|
|
232
234
|
const vendorContent = `
|
|
233
235
|
import React from 'react';
|
|
@@ -242,16 +244,15 @@ window.ReactDOM = ReactDOM;
|
|
|
242
244
|
|
|
243
245
|
const vendorOutputPath = path.join(CHUNKS_DIR, VENDOR_BUNDLE);
|
|
244
246
|
try {
|
|
245
|
-
const vendorCommand = `npx esbuild "${vendorEntryPath}" --bundle --minify --target=es2020 --format=iife --outfile="${vendorOutputPath}" --platform=browser --tree-shaking=true --define:process.env.NODE_ENV
|
|
247
|
+
const vendorCommand = `npx esbuild "${vendorEntryPath}" --bundle --minify --target=es2020 --format=iife --outfile="${vendorOutputPath}" --platform=browser --tree-shaking=true --define:process.env.NODE_ENV='"production"'`;
|
|
246
248
|
execSync(vendorCommand, { stdio: "inherit" });
|
|
247
|
-
console.log(` ā vendor.js (React + ReactDOM)`);
|
|
248
249
|
} catch (error) {
|
|
249
|
-
|
|
250
|
+
logger.error(`Failed to bundle vendor: ${error}`);
|
|
250
251
|
process.exit(1);
|
|
251
252
|
}
|
|
252
253
|
|
|
253
254
|
// Bundle each entry file separately with cache-busted names
|
|
254
|
-
|
|
255
|
+
logger.log("Bundling hydration scripts...");
|
|
255
256
|
|
|
256
257
|
// Create React shim files for aliasing
|
|
257
258
|
const SHIMS_DIR = path.join(DIST_DIR, ".shims");
|
|
@@ -301,27 +302,25 @@ module.exports = {
|
|
|
301
302
|
|
|
302
303
|
try {
|
|
303
304
|
// Use aliases to redirect React imports to window.React from vendor bundle
|
|
304
|
-
const command = `npx esbuild "${entry.path}" --bundle --minify --target=es2020 --format=iife --keep-names --outfile="${outputPath}" --platform=browser --tree-shaking=true --define:process.env.NODE_ENV
|
|
305
|
+
const command = `npx esbuild "${entry.path}" --bundle --minify --target=es2020 --format=iife --keep-names --outfile="${outputPath}" --platform=browser --tree-shaking=true --define:process.env.NODE_ENV='"production"' --alias:react=${reactShimPath} --alias:react-dom=${reactDomShimPath} --alias:react-dom/client=${reactDomClientShimPath} --alias:react/jsx-runtime=${jsxRuntimeShimPath}`;
|
|
305
306
|
execSync(command, { stdio: "inherit" });
|
|
306
307
|
manifest[entry.componentName] = chunkFilename;
|
|
307
|
-
console.log(` ā ${entry.componentName} -> ${chunkFilename}`);
|
|
308
308
|
} catch (error) {
|
|
309
|
-
|
|
309
|
+
logger.error(`Failed to bundle ${entry.componentName}: ${error}`);
|
|
310
310
|
process.exit(1);
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
313
|
|
|
314
314
|
// Write manifest file for server-side lookup
|
|
315
|
-
|
|
315
|
+
logger.log("Writing hydration manifest...");
|
|
316
316
|
fs.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2), "utf-8");
|
|
317
|
-
console.log(` ā Manifest written to ${MANIFEST_FILE}`);
|
|
318
317
|
|
|
319
318
|
// Clean up temporary entries directory
|
|
320
319
|
if (fs.existsSync(HYDRATION_ENTRIES_DIR)) {
|
|
321
320
|
fs.rmSync(HYDRATION_ENTRIES_DIR, { recursive: true });
|
|
322
321
|
}
|
|
323
322
|
|
|
324
|
-
|
|
323
|
+
logger.log("Hydration build complete!");
|
|
325
324
|
}
|
|
326
325
|
|
|
327
326
|
main();
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
import { execSync } from "child_process";
|
|
8
8
|
import * as fs from "fs";
|
|
9
9
|
import * as path from "path";
|
|
10
|
+
import { Logger } from "./logger";
|
|
10
11
|
|
|
12
|
+
const logger = new Logger("StylesBuilder");
|
|
11
13
|
const projectRoot = process.cwd();
|
|
12
14
|
const distDir = path.join(projectRoot, "dist");
|
|
13
15
|
const stylesDir = path.join(distDir, "styles");
|
|
@@ -15,7 +17,7 @@ const srcAssetsDir = path.join(projectRoot, "src/assets");
|
|
|
15
17
|
const outputCssPath = path.join(stylesDir, "styles.css");
|
|
16
18
|
|
|
17
19
|
async function main(): Promise<void> {
|
|
18
|
-
|
|
20
|
+
logger.log("Building styles...");
|
|
19
21
|
|
|
20
22
|
try {
|
|
21
23
|
// Ensure styles directory exists
|
|
@@ -24,7 +26,7 @@ async function main(): Promise<void> {
|
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
// Compile Tailwind CSS
|
|
27
|
-
|
|
29
|
+
logger.log("Compiling Tailwind CSS...");
|
|
28
30
|
execSync(
|
|
29
31
|
`NODE_ENV=production postcss ${path.join(srcAssetsDir, "styles.css")} -o ${outputCssPath}`,
|
|
30
32
|
{
|
|
@@ -32,10 +34,9 @@ async function main(): Promise<void> {
|
|
|
32
34
|
},
|
|
33
35
|
);
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
console.log("⨠Styles build complete!");
|
|
37
|
+
logger.log("Styles build complete!");
|
|
37
38
|
} catch (error: any) {
|
|
38
|
-
|
|
39
|
+
logger.error(`CSS generation failed: ${error.message}`);
|
|
39
40
|
process.exit(1);
|
|
40
41
|
}
|
|
41
42
|
}
|