Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 61f6bcebd4 | |||
| fe7e95d57f | |||
| 74c73e05f2 | |||
| 849b4f6447 | |||
| a5edc242cd | |||
| de9f8191c3 | |||
| 9802b660f9 | |||
| 6d1725ed53 | |||
| 028acc0218 | |||
| ea2e3f9f7b | |||
| 8909652860 | |||
| 38d1d4ae0c | |||
| d8a21a0c0a | |||
| f2e96640fa | |||
| 72cd9f1a00 | |||
| 2e9394c330 | |||
| f8241b49fb | |||
| 7adc7b9fb1 |
66
.gitea/workflows/default_nottags.yaml
Normal file
66
.gitea/workflows/default_nottags.yaml
Normal file
@@ -0,0 +1,66 @@
|
||||
name: Default (not tags)
|
||||
|
||||
on:
|
||||
push:
|
||||
tags-ignore:
|
||||
- '**'
|
||||
|
||||
env:
|
||||
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
|
||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
|
||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
||||
|
||||
jobs:
|
||||
security:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install pnpm and npmci
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @ship.zone/npmci
|
||||
|
||||
- name: Run npm prepare
|
||||
run: npmci npm prepare
|
||||
|
||||
- name: Audit production dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --prod
|
||||
continue-on-error: true
|
||||
|
||||
- name: Audit development dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --dev
|
||||
continue-on-error: true
|
||||
|
||||
test:
|
||||
if: ${{ always() }}
|
||||
needs: security
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Test stable
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm test
|
||||
|
||||
- name: Test build
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm build
|
||||
124
.gitea/workflows/default_tags.yaml
Normal file
124
.gitea/workflows/default_tags.yaml
Normal file
@@ -0,0 +1,124 @@
|
||||
name: Default (tags)
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
|
||||
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
|
||||
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
|
||||
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
|
||||
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
|
||||
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
|
||||
|
||||
jobs:
|
||||
security:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @ship.zone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Audit production dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --prod
|
||||
continue-on-error: true
|
||||
|
||||
- name: Audit development dependencies
|
||||
run: |
|
||||
npmci command npm config set registry https://registry.npmjs.org
|
||||
npmci command pnpm audit --audit-level=high --dev
|
||||
continue-on-error: true
|
||||
|
||||
test:
|
||||
if: ${{ always() }}
|
||||
needs: security
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @ship.zone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Test stable
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm test
|
||||
|
||||
- name: Test build
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
npmci npm build
|
||||
|
||||
release:
|
||||
needs: test
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @ship.zone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Release
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm publish
|
||||
|
||||
metadata:
|
||||
needs: test
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ env.IMAGE }}
|
||||
continue-on-error: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Prepare
|
||||
run: |
|
||||
pnpm install -g pnpm
|
||||
pnpm install -g @ship.zone/npmci
|
||||
npmci npm prepare
|
||||
|
||||
- name: Code quality
|
||||
run: |
|
||||
npmci command npm install -g typescript
|
||||
npmci npm install
|
||||
|
||||
- name: Trigger
|
||||
run: npmci trigger
|
||||
|
||||
- name: Build docs and upload artifacts
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
pnpm install -g @git.zone/tsdoc
|
||||
npmci command tsdoc
|
||||
continue-on-error: true
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -3,7 +3,6 @@
|
||||
# artifacts
|
||||
coverage/
|
||||
public/
|
||||
pages/
|
||||
|
||||
# installs
|
||||
node_modules/
|
||||
@@ -17,4 +16,8 @@ node_modules/
|
||||
dist/
|
||||
dist_*/
|
||||
|
||||
# custom
|
||||
# AI
|
||||
.claude/
|
||||
.serena/
|
||||
|
||||
#------# custom
|
||||
137
.gitlab-ci.yml
137
.gitlab-ci.yml
@@ -1,137 +0,0 @@
|
||||
# gitzone ci_default
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- .npmci_cache/
|
||||
key: '$CI_BUILD_STAGE'
|
||||
|
||||
stages:
|
||||
- security
|
||||
- test
|
||||
- release
|
||||
- metadata
|
||||
|
||||
# ====================
|
||||
# security stage
|
||||
# ====================
|
||||
mirror:
|
||||
stage: security
|
||||
script:
|
||||
- npmci git mirror
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
auditProductionDependencies:
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
stage: security
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci command npm install --production --ignore-scripts
|
||||
- npmci command npm config set registry https://registry.npmjs.org
|
||||
- npmci command npm audit --audit-level=high --only=prod --production
|
||||
tags:
|
||||
- docker
|
||||
|
||||
auditDevDependencies:
|
||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
||||
stage: security
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci command npm install --ignore-scripts
|
||||
- npmci command npm config set registry https://registry.npmjs.org
|
||||
- npmci command npm audit --audit-level=high --only=dev
|
||||
tags:
|
||||
- docker
|
||||
allow_failure: true
|
||||
|
||||
# ====================
|
||||
# test stage
|
||||
# ====================
|
||||
|
||||
testStable:
|
||||
stage: test
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci node install stable
|
||||
- npmci npm install
|
||||
- npmci npm test
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- docker
|
||||
|
||||
testBuild:
|
||||
stage: test
|
||||
script:
|
||||
- npmci npm prepare
|
||||
- npmci node install stable
|
||||
- npmci npm install
|
||||
- npmci command npm run build
|
||||
coverage: /\d+.?\d+?\%\s*coverage/
|
||||
tags:
|
||||
- docker
|
||||
|
||||
release:
|
||||
stage: release
|
||||
script:
|
||||
- npmci node install stable
|
||||
- npmci npm publish
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
# ====================
|
||||
# metadata stage
|
||||
# ====================
|
||||
codequality:
|
||||
stage: metadata
|
||||
allow_failure: true
|
||||
only:
|
||||
- tags
|
||||
script:
|
||||
- npmci command npm install -g tslint typescript
|
||||
- npmci npm prepare
|
||||
- npmci npm install
|
||||
- npmci command "tslint -c tslint.json ./ts/**/*.ts"
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- priv
|
||||
|
||||
trigger:
|
||||
stage: metadata
|
||||
script:
|
||||
- npmci trigger
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
|
||||
pages:
|
||||
stage: metadata
|
||||
script:
|
||||
- npmci node install lts
|
||||
- npmci command npm install -g @gitzone/tsdoc
|
||||
- npmci npm prepare
|
||||
- npmci npm install
|
||||
- npmci command tsdoc
|
||||
tags:
|
||||
- lossless
|
||||
- docker
|
||||
- notpriv
|
||||
only:
|
||||
- tags
|
||||
artifacts:
|
||||
expire_in: 1 week
|
||||
paths:
|
||||
- public
|
||||
allow_failure: true
|
||||
24
.vscode/launch.json
vendored
24
.vscode/launch.json
vendored
@@ -2,28 +2,10 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "current file",
|
||||
"type": "node",
|
||||
"command": "npm test",
|
||||
"name": "Run npm test",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"${relativeFile}"
|
||||
],
|
||||
"runtimeArgs": ["-r", "@gitzone/tsrun"],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"protocol": "inspector",
|
||||
"internalConsoleOptions": "openOnSessionStart"
|
||||
},
|
||||
{
|
||||
"name": "test.ts",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"test/test.ts"
|
||||
],
|
||||
"runtimeArgs": ["-r", "@gitzone/tsrun"],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"protocol": "inspector",
|
||||
"internalConsoleOptions": "openOnSessionStart"
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
40
changelog.md
Normal file
40
changelog.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-11-19 - 2.0.4 - fix(smartsitemap)
|
||||
Update CI configuration, bump dependencies, and apply small code cleanups
|
||||
|
||||
- CI: switch build image to code.foss.global, adjust NPMCI computed repo URL and install @ship.zone/npmci in workflows
|
||||
- Dependencies: bump several devDependencies and runtime dependencies to newer major/minor releases
|
||||
- package.json: add packageManager/pnpm metadata, bugs URL, homepage tweak and small script adjustments (test/build)
|
||||
- Code: use plugins.webrequest.webrequest helper for HTTP requests, formatting and typing improvements across TypeScript sources (smartsitemap, sitemapnews, sitemapwebsite)
|
||||
- Tests and fixtures: update test feed URL, normalize YAML/test formatting
|
||||
- .gitignore: add .claude and .serena ignores and reorganize custom section
|
||||
- tsconfig: add baseUrl/paths and clean up legacy compiler options
|
||||
- Misc: fix trailing commas/newlines in npmextra.json and commitinfo data
|
||||
|
||||
## 2024-05-29 - 2.0.3 - maintenance
|
||||
Release 2.0.3 with package metadata and build configuration tweaks.
|
||||
|
||||
- Update package description.
|
||||
- Update TypeScript configuration (tsconfig) for build settings.
|
||||
- Adjust npmextra.json githost entries.
|
||||
- General maintenance and housekeeping.
|
||||
|
||||
## 2023-10-20 - 2.0.2 - bugfix
|
||||
Patch release with core fixes and stability improvements.
|
||||
|
||||
- fix(core): apply core updates and bug fixes.
|
||||
|
||||
## 2022-03-24 - 2.0.0–2.0.1 - major / patch
|
||||
Major 2.0.0 release and immediate 2.0.1 follow-up containing core updates.
|
||||
|
||||
- 2.0.0: major version bump with core updates.
|
||||
- 2.0.1: subsequent fixes to address issues found after the 2.0.0 release.
|
||||
- Both releases include core maintenance and stability improvements.
|
||||
|
||||
## 2020-10-25 – 2022-03-24 - 1.0.1–1.0.15 - patch releases
|
||||
Series of patch releases and minor fixes across the 1.0.x line.
|
||||
|
||||
- Multiple incremental releases (1.0.1 through 1.0.15).
|
||||
- Repeated "fix(core): update" commits indicating small bug fixes and maintenance.
|
||||
- No large feature changes; mainly stability and housekeeping updates.
|
||||
@@ -2,17 +2,33 @@
|
||||
"gitzone": {
|
||||
"projectType": "npm",
|
||||
"module": {
|
||||
"githost": "gitlab.com",
|
||||
"gitscope": "pushrocks",
|
||||
"githost": "code.foss.global",
|
||||
"gitscope": "push.rocks",
|
||||
"gitrepo": "smartsitemap",
|
||||
"shortDescription": "a sitemap module",
|
||||
"npmPackagename": "@pushrocks/smartsitemap",
|
||||
"description": "A module for generating and managing sitemaps, supporting dynamic sitemap generation from feeds.",
|
||||
"npmPackagename": "@push.rocks/smartsitemap",
|
||||
"license": "MIT",
|
||||
"projectDomain": "push.rocks"
|
||||
"projectDomain": "push.rocks",
|
||||
"keywords": [
|
||||
"sitemap generator",
|
||||
"RSS feeds",
|
||||
"news sitemap",
|
||||
"XML sitemap",
|
||||
"website indexing",
|
||||
"search engine optimization",
|
||||
"SEO",
|
||||
"web crawling",
|
||||
"dynamic sitemap creation",
|
||||
"TypeScript",
|
||||
"node.js"
|
||||
]
|
||||
}
|
||||
},
|
||||
"npmci": {
|
||||
"npmGlobalTools": [],
|
||||
"npmAccessLevel": "public"
|
||||
},
|
||||
"tsdoc": {
|
||||
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
|
||||
}
|
||||
}
|
||||
11074
package-lock.json
generated
11074
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
65
package.json
65
package.json
@@ -1,32 +1,34 @@
|
||||
{
|
||||
"name": "@pushrocks/smartsitemap",
|
||||
"version": "1.0.13",
|
||||
"name": "@push.rocks/smartsitemap",
|
||||
"version": "2.0.4",
|
||||
"private": false,
|
||||
"description": "a sitemap module",
|
||||
"description": "A module for generating and managing sitemaps, supporting dynamic sitemap generation from feeds.",
|
||||
"main": "dist_ts/index.js",
|
||||
"typings": "dist_ts/index.d.ts",
|
||||
"type": "module",
|
||||
"author": "Lossless GmbH",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"test": "(tstest test/ --web)",
|
||||
"build": "(tsbuild --web)"
|
||||
"test": "(tstest test/ --verbose)",
|
||||
"build": "(tsbuild --allowimplicitany)",
|
||||
"buildDocs": "tsdoc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gitzone/tsbuild": "^2.1.25",
|
||||
"@gitzone/tsbundle": "^1.0.78",
|
||||
"@gitzone/tstest": "^1.0.44",
|
||||
"@pushrocks/tapbundle": "^3.2.9",
|
||||
"@types/node": "^14.14.5",
|
||||
"tslint": "^6.1.3",
|
||||
"tslint-config-prettier": "^1.15.0"
|
||||
"@git.zone/tsbuild": "^3.1.0",
|
||||
"@git.zone/tsbundle": "^2.0.8",
|
||||
"@git.zone/tsrun": "^2.0.0",
|
||||
"@git.zone/tstest": "^2.8.2",
|
||||
"@push.rocks/smartenv": "^6.0.0",
|
||||
"@push.rocks/tapbundle": "^6.0.3",
|
||||
"@types/node": "^20.8.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pushrocks/smartcache": "^1.0.13",
|
||||
"@pushrocks/smartfeed": "^1.0.5",
|
||||
"@pushrocks/smartxml": "^1.0.6",
|
||||
"@pushrocks/smartyaml": "^2.0.5",
|
||||
"@pushrocks/webrequest": "^2.0.13",
|
||||
"@tsclass/tsclass": "^3.0.25"
|
||||
"@push.rocks/smartcache": "^1.0.16",
|
||||
"@push.rocks/smartfeed": "^1.0.11",
|
||||
"@push.rocks/smartxml": "^2.0.0",
|
||||
"@push.rocks/smartyaml": "^3.0.4",
|
||||
"@push.rocks/webrequest": "^4.0.1",
|
||||
"@tsclass/tsclass": "^9.3.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 1 chrome versions"
|
||||
@@ -42,5 +44,30 @@
|
||||
"cli.js",
|
||||
"npmextra.json",
|
||||
"readme.md"
|
||||
]
|
||||
],
|
||||
"keywords": [
|
||||
"sitemap generator",
|
||||
"RSS feeds",
|
||||
"news sitemap",
|
||||
"XML sitemap",
|
||||
"website indexing",
|
||||
"search engine optimization",
|
||||
"SEO",
|
||||
"web crawling",
|
||||
"dynamic sitemap creation",
|
||||
"TypeScript",
|
||||
"node.js"
|
||||
],
|
||||
"homepage": "https://code.foss.global/push.rocks/smartsitemap#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://code.foss.global/push.rocks/smartsitemap.git"
|
||||
},
|
||||
"packageManager": "pnpm@10.18.1+sha512.77a884a165cbba2d8d1c19e3b4880eee6d2fcabd0d879121e282196b80042351d5eb3ca0935fa599da1dc51265cc68816ad2bddd2a2de5ea9fdf92adbec7cd34",
|
||||
"bugs": {
|
||||
"url": "https://code.foss.global/push.rocks/smartsitemap/issues"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {}
|
||||
}
|
||||
}
|
||||
|
||||
9754
pnpm-lock.yaml
generated
Normal file
9754
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
1
readme.hints.md
Normal file
1
readme.hints.md
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
747
readme.md
747
readme.md
@@ -1,39 +1,728 @@
|
||||
# @pushrocks/smartsitemap
|
||||
a sitemap module
|
||||
# @push.rocks/smartsitemap
|
||||
|
||||
## Availabililty and Links
|
||||
* [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/smartsitemap)
|
||||
* [gitlab.com (source)](https://gitlab.com/pushrocks/smartsitemap)
|
||||
* [github.com (source mirror)](https://github.com/pushrocks/smartsitemap)
|
||||
* [docs (typedoc)](https://pushrocks.gitlab.io/smartsitemap/)
|
||||
> 🗺️ **Modern sitemap generation and management for TypeScript** — Create XML sitemaps from RSS feeds, YAML configs, or programmatically. Perfect for SEO optimization and search engine indexing.
|
||||
|
||||
## Status for master
|
||||
## Install
|
||||
|
||||
Status Category | Status Badge
|
||||
-- | --
|
||||
GitLab Pipelines | [](https://lossless.cloud)
|
||||
GitLab Pipline Test Coverage | [](https://lossless.cloud)
|
||||
npm | [](https://lossless.cloud)
|
||||
Snyk | [](https://lossless.cloud)
|
||||
TypeScript Support | [](https://lossless.cloud)
|
||||
node Support | [](https://nodejs.org/dist/latest-v10.x/docs/api/)
|
||||
Code Style | [](https://lossless.cloud)
|
||||
PackagePhobia (total standalone install weight) | [](https://lossless.cloud)
|
||||
PackagePhobia (package size on registry) | [](https://lossless.cloud)
|
||||
BundlePhobia (total size when bundled) | [](https://lossless.cloud)
|
||||
Platform support | [](https://lossless.cloud) [](https://lossless.cloud)
|
||||
```bash
|
||||
# Using pnpm (recommended)
|
||||
pnpm install @push.rocks/smartsitemap
|
||||
|
||||
## Usage
|
||||
# Using npm
|
||||
npm install @push.rocks/smartsitemap
|
||||
|
||||
Use TypeScript for best in class intellisense
|
||||
# Using yarn
|
||||
yarn add @push.rocks/smartsitemap
|
||||
```
|
||||
|
||||
## Contribution
|
||||
## Features
|
||||
|
||||
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :)
|
||||
✨ **Multiple Sitemap Types**
|
||||
- 📰 **News Sitemaps** — Generate Google News-compatible sitemaps from RSS feeds
|
||||
- 🌐 **Website Sitemaps** — Standard XML sitemaps with customizable update frequencies
|
||||
- 🔄 **Dynamic Generation** — Create sitemaps from various sources (RSS, YAML, arrays)
|
||||
|
||||
For further information read the linked docs at the top of this readme.
|
||||
🚀 **Developer-Friendly**
|
||||
- 💪 **Full TypeScript Support** — Complete type definitions and interfaces
|
||||
- 🎯 **Simple API** — Intuitive methods for common sitemap operations
|
||||
- 📦 **Zero Configuration** — Works out of the box with sensible defaults
|
||||
- 🔍 **Sitemap Parsing** — Read and parse existing sitemaps from URLs
|
||||
|
||||
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)
|
||||
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
|
||||
🎨 **Flexible Input Sources**
|
||||
- RSS/Atom feeds (URL or string)
|
||||
- YAML configuration files
|
||||
- URL info arrays
|
||||
- Article objects
|
||||
|
||||
[](https://maintainedby.lossless.com)
|
||||
## Quick Start
|
||||
|
||||
```typescript
|
||||
import { SmartSitemap } from '@push.rocks/smartsitemap';
|
||||
|
||||
const sitemap = new SmartSitemap();
|
||||
|
||||
// Generate a news sitemap from RSS feed
|
||||
const newsSitemap = await sitemap.createSitemapNewsFromFeedUrl(
|
||||
'https://yoursite.com/rss/'
|
||||
);
|
||||
|
||||
console.log(newsSitemap);
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### 📰 Creating News Sitemaps
|
||||
|
||||
#### From RSS Feed URL
|
||||
|
||||
Perfect for blogs, news sites, and content platforms with RSS feeds:
|
||||
|
||||
```typescript
|
||||
import { SmartSitemap } from '@push.rocks/smartsitemap';
|
||||
|
||||
const sitemap = new SmartSitemap();
|
||||
|
||||
// Fetch RSS feed and generate Google News sitemap
|
||||
const newsSitemap = await sitemap.createSitemapNewsFromFeedUrl(
|
||||
'https://coffee.link/rss/'
|
||||
);
|
||||
|
||||
console.log(newsSitemap);
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:news="http://www.google.com/schemas/sitemap-news/0.9">
|
||||
<url>
|
||||
<loc>https://coffee.link/article-url/</loc>
|
||||
<news:news>
|
||||
<news:publication>
|
||||
<news:language>en</news:language>
|
||||
<news:name>some name</news:name>
|
||||
</news:publication>
|
||||
<news:keywords></news:keywords>
|
||||
<news:publication_date>2025-11-18T16:57:15.000Z</news:publication_date>
|
||||
<news:title>Article Title</news:title>
|
||||
</news:news>
|
||||
</url>
|
||||
</urlset>
|
||||
```
|
||||
|
||||
#### From RSS Feed String
|
||||
|
||||
When you already have the RSS XML content:
|
||||
|
||||
```typescript
|
||||
import { SmartSitemap } from '@push.rocks/smartsitemap';
|
||||
|
||||
const rssFeedXml = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0">
|
||||
<!-- Your RSS feed content -->
|
||||
</rss>
|
||||
`;
|
||||
|
||||
const sitemap = new SmartSitemap();
|
||||
const newsSitemap = await sitemap.createSitemapNewsFromAFeedStringArg(rssFeedXml);
|
||||
|
||||
console.log(newsSitemap);
|
||||
```
|
||||
|
||||
#### From Article Array
|
||||
|
||||
Create news sitemaps programmatically from your CMS or database:
|
||||
|
||||
```typescript
|
||||
import { SmartSitemap } from '@push.rocks/smartsitemap';
|
||||
import type { IArticle } from '@tsclass/tsclass';
|
||||
|
||||
const articles: IArticle[] = [
|
||||
{
|
||||
title: 'Breaking News Article',
|
||||
content: 'Article content here...',
|
||||
url: 'https://yoursite.com/breaking-news',
|
||||
// Add other required IArticle properties
|
||||
},
|
||||
// More articles...
|
||||
];
|
||||
|
||||
const sitemap = new SmartSitemap();
|
||||
const newsSitemap = await sitemap.createSitemapNewsFromArticleArray(articles);
|
||||
|
||||
console.log(newsSitemap);
|
||||
```
|
||||
|
||||
### 🌐 Creating Website Sitemaps
|
||||
|
||||
#### From YAML Configuration
|
||||
|
||||
Define your sitemap structure in YAML for easy maintenance:
|
||||
|
||||
```typescript
|
||||
import { SmartSitemap } from '@push.rocks/smartsitemap';
|
||||
|
||||
const yamlConfig = `
|
||||
daily:
|
||||
- https://yoursite.com/
|
||||
- https://yoursite.com/blog
|
||||
- https://yoursite.com/products
|
||||
`;
|
||||
|
||||
const sitemap = new SmartSitemap();
|
||||
const websiteSitemap = await sitemap.createSitemapFromYmlString(yamlConfig);
|
||||
|
||||
console.log(websiteSitemap);
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://yoursite.com/</loc>
|
||||
<lastmod>2025-11-19T12:00:00.000Z</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
</url>
|
||||
<!-- More URLs... -->
|
||||
</urlset>
|
||||
```
|
||||
|
||||
#### From URL Info Array
|
||||
|
||||
Fine-grained control over each URL's properties:
|
||||
|
||||
```typescript
|
||||
import { SmartSitemap, IUrlInfo } from '@push.rocks/smartsitemap';
|
||||
|
||||
const urlInfos: IUrlInfo[] = [
|
||||
{
|
||||
url: 'https://yoursite.com/',
|
||||
timestamp: Date.now(),
|
||||
frequency: 'daily', // How often the page changes
|
||||
},
|
||||
{
|
||||
url: 'https://yoursite.com/about',
|
||||
timestamp: Date.now() - 86400000, // Yesterday
|
||||
frequency: 'monthly',
|
||||
},
|
||||
{
|
||||
url: 'https://yoursite.com/blog',
|
||||
timestamp: Date.now(),
|
||||
frequency: 'weekly',
|
||||
},
|
||||
];
|
||||
|
||||
const sitemap = new SmartSitemap();
|
||||
const websiteSitemap = await sitemap.createSitemapFromUrlInfoArray(urlInfos);
|
||||
|
||||
console.log(websiteSitemap);
|
||||
```
|
||||
|
||||
**Available Frequencies:**
|
||||
- `'never'` — Archived content
|
||||
- `'daily'` — Updated every day
|
||||
- `'weekly'` — Updated weekly
|
||||
- `'monthly'` — Updated monthly
|
||||
- `'yearly'` — Updated annually
|
||||
|
||||
### 🔍 Parsing Existing Sitemaps
|
||||
|
||||
Read and parse sitemaps from remote URLs:
|
||||
|
||||
```typescript
|
||||
import { SmartSitemap } from '@push.rocks/smartsitemap';
|
||||
|
||||
const sitemap = new SmartSitemap();
|
||||
|
||||
// Parse a sitemap from URL
|
||||
const parsedSitemap = await sitemap.parseSitemapUrl(
|
||||
'https://www.theverge.com/sitemaps/google_news'
|
||||
);
|
||||
|
||||
console.log(parsedSitemap);
|
||||
// {
|
||||
// urlset: {
|
||||
// url: [
|
||||
// { loc: '...', lastmod: '...', changefreq: '...' },
|
||||
// // ...
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
```
|
||||
|
||||
#### Parse Sitemap String
|
||||
|
||||
If you already have the sitemap XML:
|
||||
|
||||
```typescript
|
||||
import { SmartSitemap } from '@push.rocks/smartsitemap';
|
||||
|
||||
const sitemapXml = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<!-- Sitemap content -->
|
||||
</urlset>
|
||||
`;
|
||||
|
||||
const sitemap = new SmartSitemap();
|
||||
const parsed = await sitemap.parseSitemap(sitemapXml);
|
||||
|
||||
console.log(parsed);
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Working with Lower-Level Classes
|
||||
|
||||
For more control, use the individual sitemap classes directly:
|
||||
|
||||
#### SitemapWebsite Class
|
||||
|
||||
```typescript
|
||||
import { SitemapWebsite, IUrlInfo } from '@push.rocks/smartsitemap';
|
||||
|
||||
const websiteSitemap = new SitemapWebsite();
|
||||
|
||||
// Add URLs one by one
|
||||
websiteSitemap.addUrl({
|
||||
url: 'https://yoursite.com/page1',
|
||||
timestamp: Date.now(),
|
||||
frequency: 'daily',
|
||||
});
|
||||
|
||||
websiteSitemap.addUrl({
|
||||
url: 'https://yoursite.com/page2',
|
||||
timestamp: Date.now(),
|
||||
frequency: 'weekly',
|
||||
});
|
||||
|
||||
// Export to XML
|
||||
const xml = websiteSitemap.exportSitemapXml();
|
||||
console.log(xml);
|
||||
```
|
||||
|
||||
#### SitemapNews Class
|
||||
|
||||
```typescript
|
||||
import { SitemapNews } from '@push.rocks/smartsitemap';
|
||||
|
||||
const newsSitemap = new SitemapNews({});
|
||||
|
||||
// Add from RSS feed URL
|
||||
await newsSitemap.readAndAddFromRssFeedUrl('https://yoursite.com/rss');
|
||||
|
||||
// Or from RSS string
|
||||
await newsSitemap.readAndAddFromRssFeedString(rssFeedXml);
|
||||
|
||||
// Or from articles
|
||||
await newsSitemap.readAndParseArticles(articles);
|
||||
|
||||
// Export to XML
|
||||
const xml = newsSitemap.exportSitemapXml();
|
||||
console.log(xml);
|
||||
```
|
||||
|
||||
### Combining Multiple Sources
|
||||
|
||||
Create comprehensive sitemaps by combining different data sources:
|
||||
|
||||
```typescript
|
||||
import { SitemapWebsite } from '@push.rocks/smartsitemap';
|
||||
|
||||
const sitemap = new SitemapWebsite();
|
||||
|
||||
// Add static pages
|
||||
sitemap.addUrl({
|
||||
url: 'https://yoursite.com/',
|
||||
timestamp: Date.now(),
|
||||
frequency: 'daily',
|
||||
});
|
||||
|
||||
// Add dynamic content from database
|
||||
const blogPosts = await fetchBlogPostsFromDB();
|
||||
for (const post of blogPosts) {
|
||||
sitemap.addUrl({
|
||||
url: post.url,
|
||||
timestamp: post.updatedAt,
|
||||
frequency: 'weekly',
|
||||
});
|
||||
}
|
||||
|
||||
// Add pages from CMS
|
||||
const cmsPages = await fetchPagesFromCMS();
|
||||
for (const page of cmsPages) {
|
||||
sitemap.addUrl({
|
||||
url: page.url,
|
||||
timestamp: page.lastModified,
|
||||
frequency: page.updateFrequency,
|
||||
});
|
||||
}
|
||||
|
||||
const xml = sitemap.exportSitemapXml();
|
||||
```
|
||||
|
||||
## TypeScript Types
|
||||
|
||||
### IUrlInfo
|
||||
|
||||
```typescript
|
||||
interface IUrlInfo {
|
||||
url: string;
|
||||
timestamp: number; // Unix timestamp in milliseconds
|
||||
frequency?: TUpdateFrequency;
|
||||
}
|
||||
|
||||
type TUpdateFrequency = 'never' | 'daily' | 'weekly' | 'monthly' | 'yearly';
|
||||
```
|
||||
|
||||
### IParsedSiteMap
|
||||
|
||||
```typescript
|
||||
interface IParsedSiteMap {
|
||||
urlset: {
|
||||
url:
|
||||
| { loc: string; lastmod: string; changefreq: string }
|
||||
| { loc: string; lastmod: string; changefreq: string }[]
|
||||
| {
|
||||
loc: string;
|
||||
'news:news': {
|
||||
'news:publication': [];
|
||||
'news:keywords': string;
|
||||
'news:publication_date': string;
|
||||
'news:title': string;
|
||||
};
|
||||
}[];
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### IRssItem
|
||||
|
||||
```typescript
|
||||
interface IRssItem {
|
||||
[key: string]: any;
|
||||
link?: string;
|
||||
guid?: string;
|
||||
title?: string;
|
||||
pubDate?: string;
|
||||
creator?: string;
|
||||
content?: string;
|
||||
isoDate?: string;
|
||||
categories?: string[];
|
||||
contentSnippet?: string;
|
||||
enclosure?: any;
|
||||
}
|
||||
```
|
||||
|
||||
## Real-World Integration Examples
|
||||
|
||||
### Express.js Server
|
||||
|
||||
```typescript
|
||||
import express from 'express';
|
||||
import { SmartSitemap } from '@push.rocks/smartsitemap';
|
||||
|
||||
const app = express();
|
||||
const sitemap = new SmartSitemap();
|
||||
|
||||
// Serve sitemap dynamically
|
||||
app.get('/sitemap.xml', async (req, res) => {
|
||||
const urlInfos = await getUrlsFromDatabase();
|
||||
const xml = await sitemap.createSitemapFromUrlInfoArray(urlInfos);
|
||||
|
||||
res.header('Content-Type', 'application/xml');
|
||||
res.send(xml);
|
||||
});
|
||||
|
||||
// News sitemap from RSS
|
||||
app.get('/news-sitemap.xml', async (req, res) => {
|
||||
const xml = await sitemap.createSitemapNewsFromFeedUrl(
|
||||
'https://yoursite.com/rss'
|
||||
);
|
||||
|
||||
res.header('Content-Type', 'application/xml');
|
||||
res.send(xml);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
```
|
||||
|
||||
### Static Site Generator
|
||||
|
||||
```typescript
|
||||
import { SmartSitemap } from '@push.rocks/smartsitemap';
|
||||
import { writeFileSync } from 'fs';
|
||||
|
||||
async function generateSitemap() {
|
||||
const sitemap = new SmartSitemap();
|
||||
|
||||
// Get all pages from your static site
|
||||
const pages = [
|
||||
{ url: 'https://yoursite.com/', frequency: 'daily' as const },
|
||||
{ url: 'https://yoursite.com/about', frequency: 'monthly' as const },
|
||||
// ... more pages
|
||||
];
|
||||
|
||||
const urlInfos = pages.map(page => ({
|
||||
url: page.url,
|
||||
timestamp: Date.now(),
|
||||
frequency: page.frequency,
|
||||
}));
|
||||
|
||||
const xml = await sitemap.createSitemapFromUrlInfoArray(urlInfos);
|
||||
|
||||
// Write to public directory
|
||||
writeFileSync('./public/sitemap.xml', xml);
|
||||
console.log('✅ Sitemap generated!');
|
||||
}
|
||||
|
||||
generateSitemap();
|
||||
```
|
||||
|
||||
### Content Management System
|
||||
|
||||
```typescript
|
||||
import { SmartSitemap, IUrlInfo } from '@push.rocks/smartsitemap';
|
||||
|
||||
class CMSSitemapGenerator {
|
||||
private sitemap = new SmartSitemap();
|
||||
|
||||
async generateCompleteSitemap(): Promise<string> {
|
||||
const urlInfos: IUrlInfo[] = [];
|
||||
|
||||
// Add all content types
|
||||
urlInfos.push(...await this.getPageUrls());
|
||||
urlInfos.push(...await this.getBlogUrls());
|
||||
urlInfos.push(...await this.getProductUrls());
|
||||
|
||||
return this.sitemap.createSitemapFromUrlInfoArray(urlInfos);
|
||||
}
|
||||
|
||||
private async getPageUrls(): Promise<IUrlInfo[]> {
|
||||
const pages = await this.cms.getPages();
|
||||
return pages.map(page => ({
|
||||
url: page.url,
|
||||
timestamp: page.updatedAt,
|
||||
frequency: 'weekly',
|
||||
}));
|
||||
}
|
||||
|
||||
private async getBlogUrls(): Promise<IUrlInfo[]> {
|
||||
const posts = await this.cms.getBlogPosts();
|
||||
return posts.map(post => ({
|
||||
url: post.url,
|
||||
timestamp: post.publishedAt,
|
||||
frequency: 'monthly',
|
||||
}));
|
||||
}
|
||||
|
||||
private async getProductUrls(): Promise<IUrlInfo[]> {
|
||||
const products = await this.cms.getProducts();
|
||||
return products.map(product => ({
|
||||
url: product.url,
|
||||
timestamp: product.lastModified,
|
||||
frequency: 'daily',
|
||||
}));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Update Frequencies
|
||||
|
||||
Choose appropriate update frequencies for better crawling:
|
||||
|
||||
```typescript
|
||||
// Homepage and main sections: daily
|
||||
{ url: 'https://yoursite.com/', frequency: 'daily' }
|
||||
|
||||
// Blog posts: weekly
|
||||
{ url: 'https://yoursite.com/blog/post', frequency: 'weekly' }
|
||||
|
||||
// Static pages: monthly or yearly
|
||||
{ url: 'https://yoursite.com/about', frequency: 'monthly' }
|
||||
|
||||
// Archived content: never
|
||||
{ url: 'https://yoursite.com/archive/2020', frequency: 'never' }
|
||||
```
|
||||
|
||||
### 2. Sitemap Size Limits
|
||||
|
||||
Google recommends keeping sitemaps under 50,000 URLs and 50MB. Split large sitemaps:
|
||||
|
||||
```typescript
|
||||
import { SitemapWebsite } from '@push.rocks/smartsitemap';
|
||||
|
||||
async function generateSitemaps(allUrls: IUrlInfo[]) {
|
||||
const chunkSize = 50000;
|
||||
const chunks = [];
|
||||
|
||||
for (let i = 0; i < allUrls.length; i += chunkSize) {
|
||||
chunks.push(allUrls.slice(i, i + chunkSize));
|
||||
}
|
||||
|
||||
const sitemaps = await Promise.all(
|
||||
chunks.map(async (chunk, index) => {
|
||||
const sitemap = new SitemapWebsite();
|
||||
chunk.forEach(url => sitemap.addUrl(url));
|
||||
const xml = sitemap.exportSitemapXml();
|
||||
|
||||
// Save as sitemap-1.xml, sitemap-2.xml, etc.
|
||||
return { filename: `sitemap-${index + 1}.xml`, content: xml };
|
||||
})
|
||||
);
|
||||
|
||||
return sitemaps;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Cache Generated Sitemaps
|
||||
|
||||
Don't regenerate on every request:
|
||||
|
||||
```typescript
|
||||
import { SmartSitemap } from '@push.rocks/smartsitemap';
|
||||
|
||||
class SitemapCache {
|
||||
private cache: string | null = null;
|
||||
private lastGenerated: number = 0;
|
||||
private cacheDuration = 3600000; // 1 hour
|
||||
|
||||
async getSitemap(): Promise<string> {
|
||||
const now = Date.now();
|
||||
|
||||
if (!this.cache || now - this.lastGenerated > this.cacheDuration) {
|
||||
const sitemap = new SmartSitemap();
|
||||
const urls = await this.fetchUrls();
|
||||
this.cache = await sitemap.createSitemapFromUrlInfoArray(urls);
|
||||
this.lastGenerated = now;
|
||||
}
|
||||
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
invalidate() {
|
||||
this.cache = null;
|
||||
}
|
||||
|
||||
private async fetchUrls() {
|
||||
// Fetch from your data source
|
||||
return [];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Submit to Search Engines
|
||||
|
||||
After generating your sitemap, submit it to search engines:
|
||||
|
||||
```typescript
|
||||
// In your robots.txt
|
||||
`
|
||||
User-agent: *
|
||||
Allow: /
|
||||
Sitemap: https://yoursite.com/sitemap.xml
|
||||
Sitemap: https://yoursite.com/news-sitemap.xml
|
||||
`
|
||||
```
|
||||
|
||||
Or ping search engines programmatically:
|
||||
|
||||
```typescript
|
||||
async function submitSitemap(sitemapUrl: string) {
|
||||
const encodedUrl = encodeURIComponent(sitemapUrl);
|
||||
|
||||
// Google
|
||||
await fetch(`https://www.google.com/ping?sitemap=${encodedUrl}`);
|
||||
|
||||
// Bing
|
||||
await fetch(`https://www.bing.com/ping?sitemap=${encodedUrl}`);
|
||||
}
|
||||
|
||||
submitSitemap('https://yoursite.com/sitemap.xml');
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### SmartSitemap
|
||||
|
||||
Main class for sitemap operations.
|
||||
|
||||
#### Methods
|
||||
|
||||
**`createSitemapNewsFromFeedUrl(feedUrl: string): Promise<string>`**
|
||||
- Generate a Google News sitemap from an RSS/Atom feed URL
|
||||
- Returns XML string
|
||||
|
||||
**`createSitemapNewsFromAFeedStringArg(feedString: string): Promise<string>`**
|
||||
- Generate a Google News sitemap from RSS/Atom feed XML string
|
||||
- Returns XML string
|
||||
|
||||
**`createSitemapNewsFromArticleArray(articles: IArticle[]): Promise<string>`**
|
||||
- Generate a Google News sitemap from an array of article objects
|
||||
- Returns XML string
|
||||
|
||||
**`createSitemapFromYmlString(yamlString: string): Promise<string>`**
|
||||
- Generate a standard sitemap from YAML configuration
|
||||
- Returns XML string
|
||||
|
||||
**`createSitemapFromUrlInfoArray(urlInfos: IUrlInfo[]): Promise<string>`**
|
||||
- Generate a standard sitemap from URL info array
|
||||
- Returns XML string
|
||||
|
||||
**`parseSitemapUrl(url: string): Promise<IParsedSiteMap>`**
|
||||
- Parse a sitemap from a URL
|
||||
- Returns parsed sitemap object
|
||||
|
||||
**`parseSitemap(sitemapXml: string): Promise<IParsedSiteMap>`**
|
||||
- Parse a sitemap from XML string
|
||||
- Returns parsed sitemap object
|
||||
|
||||
### SitemapWebsite
|
||||
|
||||
Lower-level class for website sitemaps.
|
||||
|
||||
#### Methods
|
||||
|
||||
**`addUrl(urlInfo: IUrlInfo): void`**
|
||||
- Add a URL to the sitemap
|
||||
|
||||
**`exportSitemapXml(): string`**
|
||||
- Export sitemap as XML string
|
||||
|
||||
### SitemapNews
|
||||
|
||||
Lower-level class for news sitemaps.
|
||||
|
||||
#### Methods
|
||||
|
||||
**`readAndAddFromRssFeedUrl(url: string): Promise<void>`**
|
||||
- Add news items from RSS feed URL
|
||||
|
||||
**`readAndAddFromRssFeedString(feedString: string): Promise<void>`**
|
||||
- Add news items from RSS feed string
|
||||
|
||||
**`readAndParseArticles(articles: IArticle[]): Promise<void>`**
|
||||
- Add news items from article array
|
||||
|
||||
**`exportSitemapXml(): string`**
|
||||
- Export news sitemap as XML string
|
||||
|
||||
## Why @push.rocks/smartsitemap?
|
||||
|
||||
- ✅ **Simple & Intuitive** — Clean API that just works
|
||||
- ✅ **Type-Safe** — Full TypeScript support with complete type definitions
|
||||
- ✅ **Flexible** — Multiple input sources and output formats
|
||||
- ✅ **Battle-Tested** — Used in production across multiple projects
|
||||
- ✅ **Modern** — ESM-first, async/await, latest standards
|
||||
- ✅ **SEO-Friendly** — Generates valid XML sitemaps that search engines love
|
||||
|
||||
## Related Packages
|
||||
|
||||
Part of the `@push.rocks` ecosystem:
|
||||
|
||||
- **[@push.rocks/smartfeed](https://www.npmjs.com/package/@push.rocks/smartfeed)** — RSS/Atom feed parsing
|
||||
- **[@push.rocks/smartxml](https://www.npmjs.com/package/@push.rocks/smartxml)** — XML parsing and generation
|
||||
- **[@push.rocks/smartyaml](https://www.npmjs.com/package/@push.rocks/smartyaml)** — YAML parsing
|
||||
|
||||
## License and Legal Information
|
||||
|
||||
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
||||
|
||||
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
||||
|
||||
### Trademarks
|
||||
|
||||
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
|
||||
|
||||
### Company Information
|
||||
|
||||
Task Venture Capital GmbH
|
||||
Registered at District court Bremen HRB 35230 HB, Germany
|
||||
|
||||
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
||||
|
||||
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
daily:
|
||||
- central.eu/
|
||||
- central.eu/privacy
|
||||
|
||||
18
test/test.ts
18
test/test.ts
@@ -1,21 +1,25 @@
|
||||
import { expect, tap } from '@pushrocks/tapbundle';
|
||||
import * as smartsitemap from '../ts/index';
|
||||
import { expect, tap } from '@push.rocks/tapbundle';
|
||||
import * as smartsitemap from '../ts/index.js';
|
||||
|
||||
let testSmartsitemap: smartsitemap.SmartSitemap;
|
||||
|
||||
tap.test('should create an instance of Smartsitemap', async () => {
|
||||
testSmartsitemap = new smartsitemap.SmartSitemap();
|
||||
expect(testSmartsitemap).to.be.instanceOf(smartsitemap.SmartSitemap);
|
||||
expect(testSmartsitemap).toBeInstanceOf(smartsitemap.SmartSitemap);
|
||||
});
|
||||
|
||||
tap.test('should create a sitemap from feed', async () => {
|
||||
const sitemapString = await testSmartsitemap.createSitemapNewsFromFeedUrl('https://central.eu/feed');
|
||||
const sitemapString = await testSmartsitemap.createSitemapNewsFromFeedUrl(
|
||||
'https://coffee.link/rss/',
|
||||
);
|
||||
console.log(sitemapString);
|
||||
});
|
||||
|
||||
tap.test('should parse a sitemap', async () => {
|
||||
const result = await testSmartsitemap.parseSitemapUrl('https://central.eu/sitemap-news');
|
||||
console.log(result.urlset.url);
|
||||
})
|
||||
const result = await testSmartsitemap.parseSitemapUrl(
|
||||
'https://www.theverge.com/sitemaps/google_news',
|
||||
);
|
||||
// console.log(result.urlset.url);
|
||||
});
|
||||
|
||||
tap.start();
|
||||
|
||||
8
ts/00_commitinfo_data.ts
Normal file
8
ts/00_commitinfo_data.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* autocreated commitinfo by @push.rocks/commitinfo
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartsitemap',
|
||||
version: '2.0.4',
|
||||
description: 'A module for generating and managing sitemaps, supporting dynamic sitemap generation from feeds.'
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from './smartsitemap.classes.smartsitemap';
|
||||
export * from './smartsitemap.classes.sitemapnews';
|
||||
export * from './smartsitemap.classes.sitemapwebsite';
|
||||
export * from './smartsitemap.classes.smartsitemap.js';
|
||||
export * from './smartsitemap.classes.sitemapnews.js';
|
||||
export * from './smartsitemap.classes.sitemapwebsite.js';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as plugins from './smartsitemap.plugins';
|
||||
import * as interfaces from './interfaces';
|
||||
import * as plugins from './smartsitemap.plugins.js';
|
||||
import * as interfaces from './interfaces/index.js';
|
||||
|
||||
export class SitemapNews {
|
||||
public rssItems: interfaces.IRssItem[] = [];
|
||||
@@ -8,7 +8,8 @@ export class SitemapNews {
|
||||
|
||||
public async readAndAddFromRssFeedString(feedStringArg: string) {
|
||||
const smartfeedInstance = new plugins.smartfeed.Smartfeed();
|
||||
const parsedFeed = await smartfeedInstance.parseFeedFromString(feedStringArg);
|
||||
const parsedFeed =
|
||||
await smartfeedInstance.parseFeedFromString(feedStringArg);
|
||||
this.rssItems = this.rssItems.concat(parsedFeed.items);
|
||||
}
|
||||
|
||||
@@ -18,14 +19,20 @@ export class SitemapNews {
|
||||
this.rssItems = this.rssItems.concat(parsedFeed.items);
|
||||
}
|
||||
|
||||
public async readAndParseArticles(articleArrayArg: plugins.tsclass.content.IArticle[]) {
|
||||
const rssItemArray = articleArrayArg.map((articleArg): interfaces.IRssItem => {
|
||||
return {
|
||||
title: articleArg.title,
|
||||
content: articleArg.content,
|
||||
isoDate: new Date(/* TODO: put article timestamp here */).toISOString()
|
||||
};
|
||||
});
|
||||
public async readAndParseArticles(
|
||||
articleArrayArg: plugins.tsclass.content.IArticle[],
|
||||
) {
|
||||
const rssItemArray = articleArrayArg.map(
|
||||
(articleArg): interfaces.IRssItem => {
|
||||
return {
|
||||
title: articleArg.title,
|
||||
content: articleArg.content,
|
||||
isoDate:
|
||||
new Date(/* TODO: put article timestamp here */).toISOString(),
|
||||
link: articleArg.url,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
this.rssItems = this.rssItems.concat(rssItemArray);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import * as plugins from './smartsitemap.plugins';
|
||||
import * as plugins from './smartsitemap.plugins.js';
|
||||
|
||||
export type TUpdateFrequency = 'never' | 'daily' | 'weekly' | 'monthly' | 'yearly';
|
||||
export type TUpdateFrequency =
|
||||
| 'never'
|
||||
| 'daily'
|
||||
| 'weekly'
|
||||
| 'monthly'
|
||||
| 'yearly';
|
||||
|
||||
export interface IUrlInfo {
|
||||
url: string;
|
||||
@@ -26,7 +31,7 @@ export class SitemapWebsite {
|
||||
urls.push({
|
||||
loc: urlInfoArg.url,
|
||||
lastmod: new Date(urlInfoArg.timestamp).toISOString(),
|
||||
changefreq: urlInfoArg.frequency ? urlInfoArg.frequency : 'weekly'
|
||||
changefreq: urlInfoArg.frequency ? urlInfoArg.frequency : 'weekly',
|
||||
});
|
||||
}
|
||||
const sitemapObject: any = {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { SitemapNews } from './smartsitemap.classes.sitemapnews';
|
||||
import { IUrlInfo, SitemapWebsite } from './smartsitemap.classes.sitemapwebsite';
|
||||
import * as plugins from './smartsitemap.plugins';
|
||||
import * as interfaces from './interfaces';
|
||||
import { SitemapNews } from './smartsitemap.classes.sitemapnews.js';
|
||||
import {
|
||||
type IUrlInfo,
|
||||
SitemapWebsite,
|
||||
} from './smartsitemap.classes.sitemapwebsite.js';
|
||||
import * as plugins from './smartsitemap.plugins.js';
|
||||
import * as interfaces from './interfaces/index.js';
|
||||
|
||||
export class SmartSitemap {
|
||||
constructor() {}
|
||||
@@ -9,7 +12,9 @@ export class SmartSitemap {
|
||||
/**
|
||||
* creates a sitemap for news from feedurl
|
||||
*/
|
||||
public async createSitemapNewsFromFeedUrl(feedUrlArg: string): Promise<string> {
|
||||
public async createSitemapNewsFromFeedUrl(
|
||||
feedUrlArg: string,
|
||||
): Promise<string> {
|
||||
const sitemapNewsInstance = new SitemapNews({});
|
||||
await sitemapNewsInstance.readAndAddFromRssFeedUrl(feedUrlArg);
|
||||
return sitemapNewsInstance.exportSitemapXml();
|
||||
@@ -18,7 +23,9 @@ export class SmartSitemap {
|
||||
/**
|
||||
* creates a sitemap for news from feedxmlstring
|
||||
*/
|
||||
public async createSitemapNewsFromAFeedStringArg(feedStringArg: string): Promise<string> {
|
||||
public async createSitemapNewsFromAFeedStringArg(
|
||||
feedStringArg: string,
|
||||
): Promise<string> {
|
||||
const sitemapNewsInstance = new SitemapNews({});
|
||||
await sitemapNewsInstance.readAndAddFromRssFeedString(feedStringArg);
|
||||
return sitemapNewsInstance.exportSitemapXml();
|
||||
@@ -27,7 +34,9 @@ export class SmartSitemap {
|
||||
/**
|
||||
* creates a sitemap for news from an array of articles
|
||||
*/
|
||||
public async createSitemapNewsFromArticleArray(articleArrayArg: plugins.tsclass.content.IArticle[]): Promise<string> {
|
||||
public async createSitemapNewsFromArticleArray(
|
||||
articleArrayArg: plugins.tsclass.content.IArticle[],
|
||||
): Promise<string> {
|
||||
const sitemapNewsInstance = new SitemapNews({});
|
||||
await sitemapNewsInstance.readAndParseArticles(articleArrayArg);
|
||||
return sitemapNewsInstance.exportSitemapXml();
|
||||
@@ -37,13 +46,14 @@ export class SmartSitemap {
|
||||
* creates a normal sitemap from a list of urls
|
||||
*/
|
||||
public async createSitemapFromYmlString(yamlString: string): Promise<string> {
|
||||
const yamlObject: interfaces.ISitemapYaml = await plugins.smartyaml.yamlStringToObject(yamlString);
|
||||
const yamlObject: interfaces.ISitemapYaml =
|
||||
await plugins.smartyaml.yamlStringToObject(yamlString);
|
||||
const sitemapWebsite = new SitemapWebsite();
|
||||
for(const urlArg of yamlObject.daily) {
|
||||
for (const urlArg of yamlObject.daily) {
|
||||
sitemapWebsite.addUrl({
|
||||
url: urlArg,
|
||||
timestamp: Date.now() - 10000,
|
||||
frequency: 'daily'
|
||||
frequency: 'daily',
|
||||
});
|
||||
}
|
||||
return sitemapWebsite.exportSitemapXml();
|
||||
@@ -54,7 +64,7 @@ export class SmartSitemap {
|
||||
*/
|
||||
public async createSitemapFromUrlInfoArray(urlInfosArg: IUrlInfo[]) {
|
||||
const sitemapWebsite = new SitemapWebsite();
|
||||
for(const urlInfo of urlInfosArg) {
|
||||
for (const urlInfo of urlInfosArg) {
|
||||
sitemapWebsite.addUrl(urlInfo);
|
||||
}
|
||||
return sitemapWebsite.exportSitemapXml();
|
||||
@@ -64,9 +74,8 @@ export class SmartSitemap {
|
||||
* parses a sitemap url
|
||||
*/
|
||||
public async parseSitemapUrl(urlArg: string) {
|
||||
const sitemapXml = await (await (new plugins.webrequest.WebRequest()).request(urlArg, {
|
||||
method: 'GET'
|
||||
})).text();
|
||||
const response = await plugins.webrequest.webrequest(urlArg);
|
||||
const sitemapXml = await response.text();
|
||||
|
||||
const parsedSitemap = await this.parseSitemap(sitemapXml);
|
||||
return parsedSitemap;
|
||||
@@ -75,7 +84,9 @@ export class SmartSitemap {
|
||||
/**
|
||||
* parses a sitemap
|
||||
*/
|
||||
public async parseSitemap(sitemapXmlArg: string): Promise<interfaces.IParsedSiteMap> {
|
||||
return (new plugins.smartxml.SmartXml()).parseXmlToObject(sitemapXmlArg);
|
||||
public async parseSitemap(
|
||||
sitemapXmlArg: string,
|
||||
): Promise<interfaces.IParsedSiteMap> {
|
||||
return new plugins.smartxml.SmartXml().parseXmlToObject(sitemapXmlArg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
// pushrocks scope
|
||||
import * as smartcache from '@pushrocks/smartcache';
|
||||
import * as smartfeed from '@pushrocks/smartfeed';
|
||||
import * as smartxml from '@pushrocks/smartxml';
|
||||
import * as smartyaml from '@pushrocks/smartyaml';
|
||||
import * as webrequest from '@pushrocks/webrequest';
|
||||
import * as smartcache from '@push.rocks/smartcache';
|
||||
import * as smartfeed from '@push.rocks/smartfeed';
|
||||
import * as smartxml from '@push.rocks/smartxml';
|
||||
import * as smartyaml from '@push.rocks/smartyaml';
|
||||
import * as webrequest from '@push.rocks/webrequest';
|
||||
|
||||
export {
|
||||
smartcache,
|
||||
smartfeed,
|
||||
smartxml,
|
||||
smartyaml,
|
||||
webrequest
|
||||
};
|
||||
export { smartcache, smartfeed, smartxml, smartyaml, webrequest };
|
||||
|
||||
// tsclass
|
||||
import * as tsclass from '@tsclass/tsclass';
|
||||
|
||||
export {
|
||||
tsclass
|
||||
};
|
||||
export { tsclass };
|
||||
|
||||
12
tsconfig.json
Normal file
12
tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"esModuleInterop": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {}
|
||||
},
|
||||
"exclude": ["dist_*/**/*.d.ts"]
|
||||
}
|
||||
17
tslint.json
17
tslint.json
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"extends": ["tslint:latest", "tslint-config-prettier"],
|
||||
"rules": {
|
||||
"semicolon": [true, "always"],
|
||||
"no-console": false,
|
||||
"ordered-imports": false,
|
||||
"object-literal-sort-keys": false,
|
||||
"member-ordering": {
|
||||
"options":{
|
||||
"order": [
|
||||
"static-method"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultSeverity": "warning"
|
||||
}
|
||||
Reference in New Issue
Block a user