nxt-sitemap 19.0.2
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/README.md +79 -0
- package/builders/sitemap/builder.d.ts +8 -0
- package/builders/sitemap/schema.d.ts +36 -0
- package/builders/sitemap/schema.json +76 -0
- package/builders.json +9 -0
- package/collection.json +10 -0
- package/index.d.ts +1 -0
- package/index.mjs +98 -0
- package/index.mjs.map +1 -0
- package/ng-add.cjs +77 -0
- package/ng-add.cjs.map +1 -0
- package/package.json +51 -0
- package/schematics/ng-add/index.d.ts +3 -0
- package/schematics/ng-add/schema.d.ts +19 -0
- package/schematics/ng-add/schema.json +25 -0
- package/schematics/utils.d.ts +6 -0
- package/sitemap.mjs +98 -0
- package/sitemap.mjs.map +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Angular Sitemap builder
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Liquid-JS/nxt-components/blob/master/LICENSE)
|
|
4
|
+
[](https://www.npmjs.com/package/nxt-sitemap)
|
|
5
|
+
[](https://www.npmjs.com/package/nxt-sitemap)
|
|
6
|
+
|
|
7
|
+
Angular builder for generating sitemap from prerendered routes.
|
|
8
|
+
|
|
9
|
+
## Quick links
|
|
10
|
+
|
|
11
|
+
- [Getting started](https://liquid-js.github.io/nxt-components/demo/sitemap)
|
|
12
|
+
- [API docs](https://liquid-js.github.io/nxt-components/nxt-sitemap)
|
|
13
|
+
|
|
14
|
+
### Installing
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
ng add nxt-sitemap
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Configuring the builder
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"build": {
|
|
25
|
+
...
|
|
26
|
+
},
|
|
27
|
+
"sitemap": {
|
|
28
|
+
"builder": "nxt-sitemap:sitemap",
|
|
29
|
+
"options": {
|
|
30
|
+
"srcDirectory": "./dist/demo/browser",
|
|
31
|
+
"baseUrl": "https://liquid-js.github.io/nxt-components/demo"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Generating the sitemap
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
ng run project:sitemap
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Prerendered urls in Angular
|
|
44
|
+
|
|
45
|
+
As described in [#16051](https://github.com/angular/angular/issues/16051), Angular router, by default, removes trailing slashes from urls; however, when serving prerendered pages most web servers add trailing slash which causes unneccessary redirects and degraded SEO performance. To avoid this, consider including the following code in your app configuration (based on [#16051 (comment)](https://github.com/angular/angular/issues/16051#issuecomment-575346573)):
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { DefaultUrlSerializer, UrlSerializer, UrlTree } from '@angular/router'
|
|
49
|
+
|
|
50
|
+
export class TrailingSlashUrlSerializer extends DefaultUrlSerializer {
|
|
51
|
+
override serialize(tree: UrlTree): string {
|
|
52
|
+
return this._withTrailingSlash(super.serialize(tree))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private _withTrailingSlash(url: string): string {
|
|
56
|
+
const splitOn = url.indexOf('?') > -1 ? '?' : '#'
|
|
57
|
+
const pathArr = url.split(splitOn)
|
|
58
|
+
|
|
59
|
+
if (!pathArr[0].endsWith('/')) {
|
|
60
|
+
const fileName: string = url.substring(url.lastIndexOf('/') + 1)
|
|
61
|
+
if (fileName.indexOf('.') === -1) {
|
|
62
|
+
pathArr[0] += '/'
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return pathArr.join(splitOn)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const appConfig: ApplicationConfig = {
|
|
70
|
+
providers: [
|
|
71
|
+
...
|
|
72
|
+
{
|
|
73
|
+
provide: UrlSerializer,
|
|
74
|
+
useClass: TrailingSlashUrlSerializer
|
|
75
|
+
},
|
|
76
|
+
...
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Builder } from '@angular-devkit/architect/src/internal';
|
|
2
|
+
import { JsonObject } from '@angular-devkit/core';
|
|
3
|
+
import { SitemapBuilderOptions } from './schema';
|
|
4
|
+
/**
|
|
5
|
+
* Angular builder for generating sitemap from prerendered routes
|
|
6
|
+
*/
|
|
7
|
+
declare const sitemapBuilder: Builder<SitemapBuilderOptions & JsonObject>;
|
|
8
|
+
export default sitemapBuilder;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file was automatically generated by json-schema-to-typescript.
|
|
3
|
+
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
|
|
4
|
+
* and run json-schema-to-typescript to regenerate this file.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Generate sitemap from prerendered routes
|
|
8
|
+
*/
|
|
9
|
+
export interface SitemapBuilderOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Directory where prerendered contents are located, e.g. `dist/browser`
|
|
12
|
+
*/
|
|
13
|
+
srcDirectory: string;
|
|
14
|
+
/**
|
|
15
|
+
* Base URL where the page will be deployed, e.g. https://example.com/ or https://example.com/subdir/
|
|
16
|
+
*/
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
/**
|
|
19
|
+
* Either ISO date, datetime, or a string 'today'; last modified date of the pages
|
|
20
|
+
*/
|
|
21
|
+
lastMod?: ({
|
|
22
|
+
[k: string]: unknown;
|
|
23
|
+
} | "today") & string;
|
|
24
|
+
/**
|
|
25
|
+
* Change frequency, how often the content is expected to change
|
|
26
|
+
*/
|
|
27
|
+
changeFreq?: "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
|
|
28
|
+
/**
|
|
29
|
+
* Url priority
|
|
30
|
+
*/
|
|
31
|
+
priority?: 0 | 0.1 | 0.2 | 0.3 | 0.4 | 0.5 | 0.6 | 0.7 | 0.8 | 0.9 | 1;
|
|
32
|
+
/**
|
|
33
|
+
* Include trailing slash in the URLs; disabling this option will likely cause redirects when serving prerendered pages
|
|
34
|
+
*/
|
|
35
|
+
trailingSlash?: boolean;
|
|
36
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"title": "Sitemap builder options",
|
|
5
|
+
"description": "Generate sitemap from prerendered routes",
|
|
6
|
+
"properties": {
|
|
7
|
+
"srcDirectory": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Directory where prerendered contents are located, e.g. `dist/browser`"
|
|
10
|
+
},
|
|
11
|
+
"baseUrl": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"format": "uri",
|
|
14
|
+
"description": "Base URL where the page will be deployed, e.g. https://example.com/ or https://example.com/subdir/"
|
|
15
|
+
},
|
|
16
|
+
"lastMod": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"anyOf": [
|
|
19
|
+
{
|
|
20
|
+
"format": "date-time"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"format": "date"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"enum": [
|
|
27
|
+
"today"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"description": "Either ISO date, datetime, or a string 'today'; last modified date of the pages"
|
|
32
|
+
},
|
|
33
|
+
"changeFreq": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"enum": [
|
|
36
|
+
"always",
|
|
37
|
+
"hourly",
|
|
38
|
+
"daily",
|
|
39
|
+
"weekly",
|
|
40
|
+
"monthly",
|
|
41
|
+
"yearly",
|
|
42
|
+
"never"
|
|
43
|
+
],
|
|
44
|
+
"description": "Change frequency, how often the content is expected to change"
|
|
45
|
+
},
|
|
46
|
+
"priority": {
|
|
47
|
+
"type": "number",
|
|
48
|
+
"enum": [
|
|
49
|
+
0.0,
|
|
50
|
+
0.1,
|
|
51
|
+
0.2,
|
|
52
|
+
0.3,
|
|
53
|
+
0.4,
|
|
54
|
+
0.5,
|
|
55
|
+
0.6,
|
|
56
|
+
0.7,
|
|
57
|
+
0.8,
|
|
58
|
+
0.9,
|
|
59
|
+
1.0
|
|
60
|
+
],
|
|
61
|
+
"default": 0.5,
|
|
62
|
+
"description": "Url priority"
|
|
63
|
+
},
|
|
64
|
+
"trailingSlash": {
|
|
65
|
+
"type": "boolean",
|
|
66
|
+
"default": true,
|
|
67
|
+
"description": "Include trailing slash in the URLs; disabling this option will likely cause redirects when serving prerendered pages"
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"additionalProperties": false,
|
|
71
|
+
"additionalItems": false,
|
|
72
|
+
"required": [
|
|
73
|
+
"srcDirectory",
|
|
74
|
+
"baseUrl"
|
|
75
|
+
]
|
|
76
|
+
}
|
package/builders.json
ADDED
package/collection.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",
|
|
3
|
+
"schematics": {
|
|
4
|
+
"ng-add": {
|
|
5
|
+
"description": "Add builder configuration to the project",
|
|
6
|
+
"factory": "./ng-add.cjs#ngAdd",
|
|
7
|
+
"schema": "./schematics/ng-add/schema.json"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as sitemapBuilder } from './builders/sitemap/builder';
|
package/index.mjs
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { createWriteStream } from 'fs';
|
|
2
|
+
import { readdir } from 'fs/promises';
|
|
3
|
+
import { normalize, join } from 'path';
|
|
4
|
+
import { createBuilder } from '@angular-devkit/architect';
|
|
5
|
+
import { SitemapStream } from 'sitemap';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
async function findFiles(dir, fileName, prefix = '') {
|
|
11
|
+
const results = new Array();
|
|
12
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
13
|
+
for (const entry of entries) {
|
|
14
|
+
const fullPath = join(dir, entry.name);
|
|
15
|
+
if (entry.isDirectory()) {
|
|
16
|
+
const subDirFiles = await findFiles(fullPath, fileName, join(prefix, entry.name).replace(/\\/g, '/'));
|
|
17
|
+
results.push(...subDirFiles);
|
|
18
|
+
}
|
|
19
|
+
else if (entry.isFile() && entry.name === fileName) {
|
|
20
|
+
results.push(prefix);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return results;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
function withTrailingSlash(text) {
|
|
29
|
+
if (!text.endsWith('/')) {
|
|
30
|
+
return text + '/';
|
|
31
|
+
}
|
|
32
|
+
return text;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
function streamToPromise(stream) {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
stream
|
|
40
|
+
.on('error', reject)
|
|
41
|
+
.on('finish', () => {
|
|
42
|
+
resolve();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Angular builder for generating sitemap from prerendered routes
|
|
48
|
+
*/
|
|
49
|
+
const sitemapBuilder = createBuilder(async (options, context) => {
|
|
50
|
+
const srcDirectory = withTrailingSlash(options.srcDirectory);
|
|
51
|
+
const baseUrl = new URL(withTrailingSlash(options.baseUrl));
|
|
52
|
+
const path = baseUrl.pathname;
|
|
53
|
+
const lastmod = options.lastMod == 'today'
|
|
54
|
+
? new Date()
|
|
55
|
+
: options.lastMod
|
|
56
|
+
? new Date(options.lastMod)
|
|
57
|
+
: undefined;
|
|
58
|
+
const changefreq = options.changeFreq;
|
|
59
|
+
const priority = options.priority ?? 0.5;
|
|
60
|
+
const targetFile = normalize(join(srcDirectory, 'sitemap.xml'));
|
|
61
|
+
context.reportStatus(`Generating sitemap in ${targetFile}.`);
|
|
62
|
+
try {
|
|
63
|
+
const files = await findFiles(srcDirectory, 'index.html');
|
|
64
|
+
if (!files.length) {
|
|
65
|
+
throw new Error(`No matching files found in ${srcDirectory}.`);
|
|
66
|
+
}
|
|
67
|
+
const sitemap = new SitemapStream({ hostname: options.baseUrl });
|
|
68
|
+
const target = createWriteStream(targetFile);
|
|
69
|
+
const promise = streamToPromise(sitemap.pipe(target));
|
|
70
|
+
files.forEach(file => {
|
|
71
|
+
let pathname = path + file;
|
|
72
|
+
if (!options.trailingSlash && pathname.endsWith('/'))
|
|
73
|
+
pathname = pathname.substring(0, pathname.length - 1);
|
|
74
|
+
if (options.trailingSlash)
|
|
75
|
+
pathname = withTrailingSlash(pathname);
|
|
76
|
+
sitemap.write({
|
|
77
|
+
url: pathname,
|
|
78
|
+
changefreq,
|
|
79
|
+
priority,
|
|
80
|
+
lastmod
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
sitemap.end();
|
|
84
|
+
await promise;
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
context.logger.error('Failed to generate sitemap.');
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
error: err.message
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
context.reportStatus('Done.');
|
|
94
|
+
return { success: true };
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
export { sitemapBuilder };
|
|
98
|
+
//# sourceMappingURL=index.mjs.map
|
package/index.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../../packages/packages/sitemap/src/builders/sitemap/builder.ts"],"sourcesContent":["import { createWriteStream } from 'fs'\r\nimport { readdir } from 'fs/promises'\r\nimport { join, normalize } from 'path'\r\nimport { Stream } from 'stream'\r\nimport { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect'\r\nimport { Builder } from '@angular-devkit/architect/src/internal'\r\nimport { JsonObject } from '@angular-devkit/core'\r\nimport { SitemapStream } from 'sitemap'\r\nimport { SitemapBuilderOptions } from './schema'\r\n\r\n/**\r\n * @internal\r\n */\r\nasync function findFiles(dir: string, fileName: string, prefix = ''): Promise<string[]> {\r\n const results = new Array<string>()\r\n const entries = await readdir(dir, { withFileTypes: true })\r\n\r\n for (const entry of entries) {\r\n const fullPath = join(dir, entry.name)\r\n if (entry.isDirectory()) {\r\n const subDirFiles = await findFiles(fullPath, fileName, join(prefix, entry.name).replace(/\\\\/g, '/'))\r\n results.push(...subDirFiles)\r\n } else if (entry.isFile() && entry.name === fileName) {\r\n results.push(prefix)\r\n }\r\n }\r\n\r\n return results\r\n}\r\n\r\n/**\r\n * @internal\r\n */\r\nfunction withTrailingSlash(text: string) {\r\n if (!text.endsWith('/')) {\r\n return text + '/'\r\n }\r\n return text\r\n}\r\n\r\n/**\r\n * @internal\r\n */\r\nfunction streamToPromise(stream: Stream) {\r\n return new Promise<void>((resolve, reject) => {\r\n stream\r\n .on('error', reject)\r\n .on('finish', () => {\r\n resolve()\r\n })\r\n })\r\n}\r\n\r\n/**\r\n * Angular builder for generating sitemap from prerendered routes\r\n */\r\nconst sitemapBuilder: Builder<SitemapBuilderOptions & JsonObject> = createBuilder(async (options: SitemapBuilderOptions, context: BuilderContext): Promise<BuilderOutput> => {\r\n const srcDirectory = withTrailingSlash(options.srcDirectory)\r\n const baseUrl = new URL(withTrailingSlash(options.baseUrl))\r\n const path = baseUrl.pathname\r\n const lastmod = options.lastMod == 'today'\r\n ? new Date()\r\n : options.lastMod\r\n ? new Date(options.lastMod)\r\n : undefined\r\n\r\n const changefreq = options.changeFreq\r\n const priority = options.priority ?? 0.5\r\n\r\n const targetFile = normalize(join(srcDirectory, 'sitemap.xml'))\r\n\r\n context.reportStatus(`Generating sitemap in ${targetFile}.`)\r\n\r\n try {\r\n const files = await findFiles(srcDirectory, 'index.html')\r\n\r\n if (!files.length) {\r\n throw new Error(`No matching files found in ${srcDirectory}.`)\r\n }\r\n\r\n const sitemap = new SitemapStream({ hostname: options.baseUrl })\r\n const target = createWriteStream(targetFile)\r\n const promise = streamToPromise(sitemap.pipe(target))\r\n\r\n files.forEach(file => {\r\n let pathname = path + file\r\n if (!options.trailingSlash && pathname.endsWith('/'))\r\n pathname = pathname.substring(0, pathname.length - 1)\r\n if (options.trailingSlash)\r\n pathname = withTrailingSlash(pathname)\r\n\r\n sitemap.write({\r\n url: pathname,\r\n changefreq,\r\n priority,\r\n lastmod\r\n })\r\n })\r\n sitemap.end()\r\n\r\n await promise\r\n } catch (err) {\r\n context.logger.error('Failed to generate sitemap.')\r\n return {\r\n success: false,\r\n error: (err as Error).message\r\n }\r\n }\r\n\r\n context.reportStatus('Done.')\r\n return { success: true }\r\n})\r\n\r\nexport default sitemapBuilder\r\n"],"names":[],"mappings":";;;;;;AAUA;;AAEG;AACH,eAAe,SAAS,CAAC,GAAW,EAAE,QAAgB,EAAE,MAAM,GAAG,EAAE,EAAA;AAC/D,IAAA,MAAM,OAAO,GAAG,IAAI,KAAK,EAAU;AACnC,IAAA,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;AAE3D,IAAA,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC;AACtC,QAAA,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE;YACrB,MAAM,WAAW,GAAG,MAAM,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACrG,YAAA,OAAO,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC;;aACzB,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE;AAClD,YAAA,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;;;AAI5B,IAAA,OAAO,OAAO;AAClB;AAEA;;AAEG;AACH,SAAS,iBAAiB,CAAC,IAAY,EAAA;IACnC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;QACrB,OAAO,IAAI,GAAG,GAAG;;AAErB,IAAA,OAAO,IAAI;AACf;AAEA;;AAEG;AACH,SAAS,eAAe,CAAC,MAAc,EAAA;IACnC,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,KAAI;QACzC;AACK,aAAA,EAAE,CAAC,OAAO,EAAE,MAAM;AAClB,aAAA,EAAE,CAAC,QAAQ,EAAE,MAAK;AACf,YAAA,OAAO,EAAE;AACb,SAAC,CAAC;AACV,KAAC,CAAC;AACN;AAEA;;AAEG;AACG,MAAA,cAAc,GAAgD,aAAa,CAAC,OAAO,OAA8B,EAAE,OAAuB,KAA4B;IACxK,MAAM,YAAY,GAAG,iBAAiB,CAAC,OAAO,CAAC,YAAY,CAAC;AAC5D,IAAA,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,iBAAiB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AAC3D,IAAA,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ;AAC7B,IAAA,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI;UAC7B,IAAI,IAAI;UACR,OAAO,CAAC;AACN,cAAE,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO;cACxB,SAAS;AAEnB,IAAA,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU;AACrC,IAAA,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,GAAG;IAExC,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;AAE/D,IAAA,OAAO,CAAC,YAAY,CAAC,yBAAyB,UAAU,CAAA,CAAA,CAAG,CAAC;AAE5D,IAAA,IAAI;QACA,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,YAAY,EAAE,YAAY,CAAC;AAEzD,QAAA,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;AACf,YAAA,MAAM,IAAI,KAAK,CAAC,8BAA8B,YAAY,CAAA,CAAA,CAAG,CAAC;;AAGlE,QAAA,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;AAChE,QAAA,MAAM,MAAM,GAAG,iBAAiB,CAAC,UAAU,CAAC;QAC5C,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAErD,QAAA,KAAK,CAAC,OAAO,CAAC,IAAI,IAAG;AACjB,YAAA,IAAI,QAAQ,GAAG,IAAI,GAAG,IAAI;YAC1B,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;AAChD,gBAAA,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YACzD,IAAI,OAAO,CAAC,aAAa;AACrB,gBAAA,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,CAAC;YAE1C,OAAO,CAAC,KAAK,CAAC;AACV,gBAAA,GAAG,EAAE,QAAQ;gBACb,UAAU;gBACV,QAAQ;gBACR;AACH,aAAA,CAAC;AACN,SAAC,CAAC;QACF,OAAO,CAAC,GAAG,EAAE;AAEb,QAAA,MAAM,OAAO;;IACf,OAAO,GAAG,EAAE;AACV,QAAA,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,CAAC;QACnD,OAAO;AACH,YAAA,OAAO,EAAE,KAAK;YACd,KAAK,EAAG,GAAa,CAAC;SACzB;;AAGL,IAAA,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC;AAC7B,IAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE;AAC5B,CAAC;;;;"}
|
package/ng-add.cjs
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var path = require('path');
|
|
4
|
+
var core = require('@angular-devkit/core');
|
|
5
|
+
var schematics = require('@angular-devkit/schematics');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
function createHost(tree) {
|
|
11
|
+
return {
|
|
12
|
+
readFile: async (path) => {
|
|
13
|
+
const data = tree.read(path);
|
|
14
|
+
if (!data) {
|
|
15
|
+
throw new schematics.SchematicsException('File not found.');
|
|
16
|
+
}
|
|
17
|
+
return core.virtualFs.fileBufferToString(data);
|
|
18
|
+
},
|
|
19
|
+
writeFile: async (path, data) => tree.overwrite(path, data),
|
|
20
|
+
isDirectory: async (path) => !tree.exists(path) && tree.getDir(path).subfiles.length > 0,
|
|
21
|
+
isFile: async (path) => tree.exists(path)
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function ngAdd(options) {
|
|
26
|
+
return async (tree, _context) => {
|
|
27
|
+
const host = createHost(tree);
|
|
28
|
+
const { workspace } = await core.workspaces.readWorkspace('/', host);
|
|
29
|
+
if (!options.project) {
|
|
30
|
+
const apps = Array.from(workspace.projects.values()).filter(v => v.extensions['projectType'] === 'application');
|
|
31
|
+
if (apps.length === 1) {
|
|
32
|
+
// If there is only one project, return that one.
|
|
33
|
+
options.project = Array.from(workspace.projects.keys())[0];
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
throw new schematics.SchematicsException('There is more than one project in your workspace. Please select it manually by using the --project argument.');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const project = workspace.projects.get(options.project);
|
|
40
|
+
if (!project) {
|
|
41
|
+
throw new schematics.SchematicsException('The specified Angular project is not defined in this workspace');
|
|
42
|
+
}
|
|
43
|
+
if (project.extensions['projectType'] !== 'application') {
|
|
44
|
+
throw new schematics.SchematicsException('Deploy requires an Angular project type of "application" in angular.json');
|
|
45
|
+
}
|
|
46
|
+
if (!options.srcDirectory) {
|
|
47
|
+
const outputPath = project.targets.get('build')?.options?.['outputPath'];
|
|
48
|
+
if (!outputPath) {
|
|
49
|
+
throw new schematics.SchematicsException(`Cannot read the output path (architect.build.options.outputPath) of the Angular project "${options.project}" in angular.json`);
|
|
50
|
+
}
|
|
51
|
+
options.srcDirectory = '';
|
|
52
|
+
if (typeof outputPath == 'string') {
|
|
53
|
+
options.srcDirectory = path.join(outputPath, 'browser');
|
|
54
|
+
}
|
|
55
|
+
else if (typeof outputPath == 'object' && !Array.isArray(outputPath)) {
|
|
56
|
+
options.srcDirectory = '';
|
|
57
|
+
if (outputPath['base']) {
|
|
58
|
+
options.srcDirectory = outputPath['base'];
|
|
59
|
+
}
|
|
60
|
+
options.srcDirectory = path.normalize(path.join(options.srcDirectory, outputPath['browser'] ?? 'browser')).replace(/\\/g, '/');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
project.targets.add({
|
|
64
|
+
name: 'sitemap',
|
|
65
|
+
builder: 'nxt-sitemap:sitemap',
|
|
66
|
+
options: {
|
|
67
|
+
srcDirectory: options.srcDirectory,
|
|
68
|
+
baseUrl: options.baseUrl
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
await core.workspaces.writeWorkspace(workspace, host);
|
|
72
|
+
return tree;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
exports.ngAdd = ngAdd;
|
|
77
|
+
//# sourceMappingURL=ng-add.cjs.map
|
package/ng-add.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ng-add.cjs","sources":["../../packages/packages/sitemap/src/schematics/utils.ts","../../packages/packages/sitemap/src/schematics/ng-add/index.ts"],"sourcesContent":["import { virtualFs, workspaces } from '@angular-devkit/core'\r\nimport { SchematicsException, Tree } from '@angular-devkit/schematics'\r\n\r\n/**\r\n * @internal\r\n */\r\nexport function createHost(tree: Tree): workspaces.WorkspaceHost {\r\n return {\r\n readFile: async (path: string): Promise<string> => {\r\n const data = tree.read(path)\r\n if (!data) {\r\n throw new SchematicsException('File not found.')\r\n }\r\n return virtualFs.fileBufferToString(data)\r\n },\r\n writeFile: async (path: string, data: string): Promise<void> => tree.overwrite(path, data),\r\n isDirectory: async (path: string): Promise<boolean> => !tree.exists(path) && tree.getDir(path).subfiles.length > 0,\r\n isFile: async (path: string): Promise<boolean> => tree.exists(path)\r\n }\r\n}\r\n","import { join, normalize } from 'path'\r\nimport { workspaces } from '@angular-devkit/core'\r\nimport { SchematicContext, SchematicsException, Tree } from '@angular-devkit/schematics'\r\nimport { createHost } from '../utils'\r\nimport { NgAddSchematicOptions } from './schema'\r\n\r\nexport function ngAdd(options: NgAddSchematicOptions) {\r\n return async (tree: Tree, _context: SchematicContext) => {\r\n const host = createHost(tree)\r\n const { workspace } = await workspaces.readWorkspace('/', host)\r\n\r\n if (!options.project) {\r\n const apps = Array.from(workspace.projects.values()).filter(v => v.extensions['projectType'] === 'application')\r\n if (apps.length === 1) {\r\n // If there is only one project, return that one.\r\n options.project = Array.from(workspace.projects.keys())[0]\r\n } else {\r\n throw new SchematicsException(\r\n 'There is more than one project in your workspace. Please select it manually by using the --project argument.'\r\n )\r\n }\r\n }\r\n\r\n const project = workspace.projects.get(options.project)\r\n if (!project) {\r\n throw new SchematicsException(\r\n 'The specified Angular project is not defined in this workspace'\r\n )\r\n }\r\n\r\n if (project.extensions['projectType'] !== 'application') {\r\n throw new SchematicsException(\r\n 'Deploy requires an Angular project type of \"application\" in angular.json'\r\n )\r\n }\r\n\r\n if (!options.srcDirectory) {\r\n const outputPath = project.targets.get('build')?.options?.['outputPath']\r\n if (!outputPath) {\r\n throw new SchematicsException(\r\n `Cannot read the output path (architect.build.options.outputPath) of the Angular project \"${options.project}\" in angular.json`\r\n )\r\n }\r\n options.srcDirectory = ''\r\n if (typeof outputPath == 'string') {\r\n options.srcDirectory = join(outputPath, 'browser')\r\n } else if (typeof outputPath == 'object' && !Array.isArray(outputPath)) {\r\n options.srcDirectory = ''\r\n if (outputPath['base'] as any) {\r\n options.srcDirectory = outputPath['base'] as any\r\n }\r\n options.srcDirectory = normalize(join(options.srcDirectory!, outputPath['browser'] as any ?? 'browser')).replace(/\\\\/g, '/')\r\n }\r\n }\r\n\r\n project.targets.add({\r\n name: 'sitemap',\r\n builder: 'nxt-sitemap:sitemap',\r\n options: {\r\n srcDirectory: options.srcDirectory,\r\n baseUrl: options.baseUrl\r\n }\r\n })\r\n\r\n await workspaces.writeWorkspace(workspace, host)\r\n return tree\r\n }\r\n}\r\n"],"names":["SchematicsException","virtualFs","workspaces","join","normalize"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nxt-sitemap",
|
|
3
|
+
"description": "Angular builder for generating sitemap from prerendered routes.",
|
|
4
|
+
"bugs": "https://github.com/Liquid-JS/nxt-components/issues",
|
|
5
|
+
"version": "19.0.2",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"repository": "https://github.com/Liquid-JS/nxt-components.git",
|
|
9
|
+
"homepage": "https://github.com/Liquid-JS/nxt-components/tree/master/packages/sitemap",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"nxt-sitemap",
|
|
12
|
+
"ng-sitemap",
|
|
13
|
+
"angular-sitemap",
|
|
14
|
+
"angular universal",
|
|
15
|
+
"angular SSR",
|
|
16
|
+
"prerendering",
|
|
17
|
+
"sitemap",
|
|
18
|
+
"generator"
|
|
19
|
+
],
|
|
20
|
+
"builders": "./builders.json",
|
|
21
|
+
"schematics": "./collection.json",
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@angular-devkit/architect": "^0.1900.0",
|
|
24
|
+
"@angular-devkit/core": "^19.0.0",
|
|
25
|
+
"@angular-devkit/schematics": "^19.0.0"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"sitemap": "^8.0.0",
|
|
29
|
+
"tslib": "^2.8.1"
|
|
30
|
+
},
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./index.d.ts",
|
|
34
|
+
"default": "./index.mjs"
|
|
35
|
+
},
|
|
36
|
+
"./package.json": {
|
|
37
|
+
"default": "./package.json"
|
|
38
|
+
},
|
|
39
|
+
"./sitemap": {
|
|
40
|
+
"types": "./builders/sitemap/builder.d.ts",
|
|
41
|
+
"default": "./sitemap.mjs"
|
|
42
|
+
},
|
|
43
|
+
"./ng-add": {
|
|
44
|
+
"types": "./schematics/ng-add/index.d.ts",
|
|
45
|
+
"default": "./ng-add.cjs"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"module": "./index.mjs",
|
|
49
|
+
"typings": "./index.d.ts",
|
|
50
|
+
"sideEffects": false
|
|
51
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { SchematicContext, Tree } from '@angular-devkit/schematics';
|
|
2
|
+
import { NgAddSchematicOptions } from './schema';
|
|
3
|
+
export declare function ngAdd(options: NgAddSchematicOptions): (tree: Tree, _context: SchematicContext) => Promise<import("@angular-devkit/schematics/src/tree/interface").Tree>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file was automatically generated by json-schema-to-typescript.
|
|
3
|
+
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
|
|
4
|
+
* and run json-schema-to-typescript to regenerate this file.
|
|
5
|
+
*/
|
|
6
|
+
export interface NgAddSchematicOptions {
|
|
7
|
+
/**
|
|
8
|
+
* The project for which the sitemap config should be added
|
|
9
|
+
*/
|
|
10
|
+
project?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Directory where prerendered contents are located, e.g. `dist/browser`
|
|
13
|
+
*/
|
|
14
|
+
srcDirectory?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Base URL where the page will be deployed, e.g. https://example.com/ or https://example.com/subdir/
|
|
17
|
+
*/
|
|
18
|
+
baseUrl: string;
|
|
19
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"title": "NgAdd schematic options",
|
|
5
|
+
"properties": {
|
|
6
|
+
"project": {
|
|
7
|
+
"type": "string",
|
|
8
|
+
"description": "The project for which the sitemap config should be added"
|
|
9
|
+
},
|
|
10
|
+
"srcDirectory": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Directory where prerendered contents are located, e.g. `dist/browser`"
|
|
13
|
+
},
|
|
14
|
+
"baseUrl": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"format": "uri",
|
|
17
|
+
"description": "Base URL where the page will be deployed, e.g. https://example.com/ or https://example.com/subdir/",
|
|
18
|
+
"x-prompt": "What is the base URL of your site?"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"required": [
|
|
22
|
+
"baseUrl"
|
|
23
|
+
],
|
|
24
|
+
"additionalProperties": false
|
|
25
|
+
}
|
package/sitemap.mjs
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { createWriteStream } from 'fs';
|
|
2
|
+
import { readdir } from 'fs/promises';
|
|
3
|
+
import { normalize, join } from 'path';
|
|
4
|
+
import { createBuilder } from '@angular-devkit/architect';
|
|
5
|
+
import { SitemapStream } from 'sitemap';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
async function findFiles(dir, fileName, prefix = '') {
|
|
11
|
+
const results = new Array();
|
|
12
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
13
|
+
for (const entry of entries) {
|
|
14
|
+
const fullPath = join(dir, entry.name);
|
|
15
|
+
if (entry.isDirectory()) {
|
|
16
|
+
const subDirFiles = await findFiles(fullPath, fileName, join(prefix, entry.name).replace(/\\/g, '/'));
|
|
17
|
+
results.push(...subDirFiles);
|
|
18
|
+
}
|
|
19
|
+
else if (entry.isFile() && entry.name === fileName) {
|
|
20
|
+
results.push(prefix);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return results;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
function withTrailingSlash(text) {
|
|
29
|
+
if (!text.endsWith('/')) {
|
|
30
|
+
return text + '/';
|
|
31
|
+
}
|
|
32
|
+
return text;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
function streamToPromise(stream) {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
stream
|
|
40
|
+
.on('error', reject)
|
|
41
|
+
.on('finish', () => {
|
|
42
|
+
resolve();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Angular builder for generating sitemap from prerendered routes
|
|
48
|
+
*/
|
|
49
|
+
const sitemapBuilder = createBuilder(async (options, context) => {
|
|
50
|
+
const srcDirectory = withTrailingSlash(options.srcDirectory);
|
|
51
|
+
const baseUrl = new URL(withTrailingSlash(options.baseUrl));
|
|
52
|
+
const path = baseUrl.pathname;
|
|
53
|
+
const lastmod = options.lastMod == 'today'
|
|
54
|
+
? new Date()
|
|
55
|
+
: options.lastMod
|
|
56
|
+
? new Date(options.lastMod)
|
|
57
|
+
: undefined;
|
|
58
|
+
const changefreq = options.changeFreq;
|
|
59
|
+
const priority = options.priority ?? 0.5;
|
|
60
|
+
const targetFile = normalize(join(srcDirectory, 'sitemap.xml'));
|
|
61
|
+
context.reportStatus(`Generating sitemap in ${targetFile}.`);
|
|
62
|
+
try {
|
|
63
|
+
const files = await findFiles(srcDirectory, 'index.html');
|
|
64
|
+
if (!files.length) {
|
|
65
|
+
throw new Error(`No matching files found in ${srcDirectory}.`);
|
|
66
|
+
}
|
|
67
|
+
const sitemap = new SitemapStream({ hostname: options.baseUrl });
|
|
68
|
+
const target = createWriteStream(targetFile);
|
|
69
|
+
const promise = streamToPromise(sitemap.pipe(target));
|
|
70
|
+
files.forEach(file => {
|
|
71
|
+
let pathname = path + file;
|
|
72
|
+
if (!options.trailingSlash && pathname.endsWith('/'))
|
|
73
|
+
pathname = pathname.substring(0, pathname.length - 1);
|
|
74
|
+
if (options.trailingSlash)
|
|
75
|
+
pathname = withTrailingSlash(pathname);
|
|
76
|
+
sitemap.write({
|
|
77
|
+
url: pathname,
|
|
78
|
+
changefreq,
|
|
79
|
+
priority,
|
|
80
|
+
lastmod
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
sitemap.end();
|
|
84
|
+
await promise;
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
context.logger.error('Failed to generate sitemap.');
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
error: err.message
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
context.reportStatus('Done.');
|
|
94
|
+
return { success: true };
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
export { sitemapBuilder as default };
|
|
98
|
+
//# sourceMappingURL=sitemap.mjs.map
|
package/sitemap.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sitemap.mjs","sources":["../../packages/packages/sitemap/src/builders/sitemap/builder.ts"],"sourcesContent":["import { createWriteStream } from 'fs'\r\nimport { readdir } from 'fs/promises'\r\nimport { join, normalize } from 'path'\r\nimport { Stream } from 'stream'\r\nimport { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect'\r\nimport { Builder } from '@angular-devkit/architect/src/internal'\r\nimport { JsonObject } from '@angular-devkit/core'\r\nimport { SitemapStream } from 'sitemap'\r\nimport { SitemapBuilderOptions } from './schema'\r\n\r\n/**\r\n * @internal\r\n */\r\nasync function findFiles(dir: string, fileName: string, prefix = ''): Promise<string[]> {\r\n const results = new Array<string>()\r\n const entries = await readdir(dir, { withFileTypes: true })\r\n\r\n for (const entry of entries) {\r\n const fullPath = join(dir, entry.name)\r\n if (entry.isDirectory()) {\r\n const subDirFiles = await findFiles(fullPath, fileName, join(prefix, entry.name).replace(/\\\\/g, '/'))\r\n results.push(...subDirFiles)\r\n } else if (entry.isFile() && entry.name === fileName) {\r\n results.push(prefix)\r\n }\r\n }\r\n\r\n return results\r\n}\r\n\r\n/**\r\n * @internal\r\n */\r\nfunction withTrailingSlash(text: string) {\r\n if (!text.endsWith('/')) {\r\n return text + '/'\r\n }\r\n return text\r\n}\r\n\r\n/**\r\n * @internal\r\n */\r\nfunction streamToPromise(stream: Stream) {\r\n return new Promise<void>((resolve, reject) => {\r\n stream\r\n .on('error', reject)\r\n .on('finish', () => {\r\n resolve()\r\n })\r\n })\r\n}\r\n\r\n/**\r\n * Angular builder for generating sitemap from prerendered routes\r\n */\r\nconst sitemapBuilder: Builder<SitemapBuilderOptions & JsonObject> = createBuilder(async (options: SitemapBuilderOptions, context: BuilderContext): Promise<BuilderOutput> => {\r\n const srcDirectory = withTrailingSlash(options.srcDirectory)\r\n const baseUrl = new URL(withTrailingSlash(options.baseUrl))\r\n const path = baseUrl.pathname\r\n const lastmod = options.lastMod == 'today'\r\n ? new Date()\r\n : options.lastMod\r\n ? new Date(options.lastMod)\r\n : undefined\r\n\r\n const changefreq = options.changeFreq\r\n const priority = options.priority ?? 0.5\r\n\r\n const targetFile = normalize(join(srcDirectory, 'sitemap.xml'))\r\n\r\n context.reportStatus(`Generating sitemap in ${targetFile}.`)\r\n\r\n try {\r\n const files = await findFiles(srcDirectory, 'index.html')\r\n\r\n if (!files.length) {\r\n throw new Error(`No matching files found in ${srcDirectory}.`)\r\n }\r\n\r\n const sitemap = new SitemapStream({ hostname: options.baseUrl })\r\n const target = createWriteStream(targetFile)\r\n const promise = streamToPromise(sitemap.pipe(target))\r\n\r\n files.forEach(file => {\r\n let pathname = path + file\r\n if (!options.trailingSlash && pathname.endsWith('/'))\r\n pathname = pathname.substring(0, pathname.length - 1)\r\n if (options.trailingSlash)\r\n pathname = withTrailingSlash(pathname)\r\n\r\n sitemap.write({\r\n url: pathname,\r\n changefreq,\r\n priority,\r\n lastmod\r\n })\r\n })\r\n sitemap.end()\r\n\r\n await promise\r\n } catch (err) {\r\n context.logger.error('Failed to generate sitemap.')\r\n return {\r\n success: false,\r\n error: (err as Error).message\r\n }\r\n }\r\n\r\n context.reportStatus('Done.')\r\n return { success: true }\r\n})\r\n\r\nexport default sitemapBuilder\r\n"],"names":[],"mappings":"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"}
|