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 ADDED
@@ -0,0 +1,79 @@
1
+ # Angular Sitemap builder
2
+
3
+ [![GitHub license](https://img.shields.io/github/license/Liquid-JS/nxt-components.svg)](https://github.com/Liquid-JS/nxt-components/blob/master/LICENSE)
4
+ [![npm](https://img.shields.io/npm/dm/nxt-sitemap.svg)](https://www.npmjs.com/package/nxt-sitemap)
5
+ [![scope](https://img.shields.io/npm/v/nxt-sitemap.svg)](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
@@ -0,0 +1,9 @@
1
+ {
2
+ "builders": {
3
+ "sitemap": {
4
+ "implementation": "./sitemap.mjs",
5
+ "schema": "./builders/sitemap/schema.json",
6
+ "description": "Generate sitemap from prerendered routes"
7
+ }
8
+ }
9
+ }
@@ -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":"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"}
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
+ }
@@ -0,0 +1,6 @@
1
+ import { workspaces } from '@angular-devkit/core';
2
+ import { Tree } from '@angular-devkit/schematics';
3
+ /**
4
+ * @internal
5
+ */
6
+ export declare function createHost(tree: Tree): workspaces.WorkspaceHost;
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
@@ -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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"}