webspresso 0.0.30 → 0.0.32
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 +44 -1
- package/core/orm/schema-helpers.js +17 -4
- package/package.json +1 -1
- package/plugins/index.js +2 -0
- package/plugins/seo-checker/analyzer.js +871 -0
- package/plugins/seo-checker/checks.js +384 -0
- package/plugins/seo-checker/index.js +99 -0
- package/plugins/seo-checker/panel.js +471 -0
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ A minimal, file-based SSR framework for Node.js with Nunjucks templating.
|
|
|
12
12
|
- **Lifecycle Hooks**: Global and route-level hooks for request processing
|
|
13
13
|
- **Template Helpers**: Laravel-inspired helper functions available in templates
|
|
14
14
|
- **Plugin System**: Extensible architecture with version control and inter-plugin communication
|
|
15
|
-
- **Built-in Plugins**: Development dashboard, sitemap generator, analytics integration (Google, Yandex, Bing)
|
|
15
|
+
- **Built-in Plugins**: Development dashboard, sitemap generator, SEO checker, analytics integration (Google, Yandex, Bing)
|
|
16
16
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
@@ -527,6 +527,49 @@ Template helpers from analytics plugin:
|
|
|
527
527
|
|
|
528
528
|
Individual helpers: `gtag()`, `gtm()`, `gtmNoscript()`, `yandexMetrika()`, `bingUET()`, `facebookPixel()`, `allAnalytics()`
|
|
529
529
|
|
|
530
|
+
**SEO Checker Plugin:**
|
|
531
|
+
- Client-side SEO analysis tool (inspired by django-check-seo)
|
|
532
|
+
- Integrated with dev toolbar
|
|
533
|
+
- 40+ SEO checks across 7 categories
|
|
534
|
+
- Real-time analysis with score calculation
|
|
535
|
+
- Only active in development mode
|
|
536
|
+
|
|
537
|
+
```javascript
|
|
538
|
+
const { seoCheckerPlugin } = require('webspresso/plugins');
|
|
539
|
+
|
|
540
|
+
const { app } = createApp({
|
|
541
|
+
pagesDir: './pages',
|
|
542
|
+
plugins: [
|
|
543
|
+
seoCheckerPlugin({
|
|
544
|
+
settings: {
|
|
545
|
+
titleLength: [30, 60], // Min/max title length
|
|
546
|
+
descriptionLength: [50, 160], // Min/max description length
|
|
547
|
+
minContentWords: 300, // Minimum content words
|
|
548
|
+
minInternalLinks: 1, // Minimum internal links
|
|
549
|
+
minExternalLinks: 1, // Minimum external links
|
|
550
|
+
maxUrlLength: 75, // Maximum URL length
|
|
551
|
+
maxUrlDepth: 3 // Maximum URL depth
|
|
552
|
+
}
|
|
553
|
+
})
|
|
554
|
+
]
|
|
555
|
+
});
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
SEO Check Categories:
|
|
559
|
+
| Category | Checks |
|
|
560
|
+
|----------|--------|
|
|
561
|
+
| **Meta** | Title, Description, Canonical, Viewport, Robots, Charset, Lang |
|
|
562
|
+
| **Headings** | H1 existence, Single H1, Hierarchy, Non-empty headings |
|
|
563
|
+
| **Content** | Word count, Paragraphs, Keyword usage, Keywords early |
|
|
564
|
+
| **Links** | Internal links, External links, Nofollow, Anchor text |
|
|
565
|
+
| **Images** | Alt text, Descriptive alt, Dimensions, Lazy loading |
|
|
566
|
+
| **Structured** | Open Graph, Twitter Card, JSON-LD, Hreflang |
|
|
567
|
+
| **URL** | Length, Depth, Readability, HTTPS |
|
|
568
|
+
|
|
569
|
+
The SEO Checker panel appears as a floating widget and can be opened via:
|
|
570
|
+
- Dev toolbar "SEO Check" button
|
|
571
|
+
- Floating toggle button (🔍) in bottom-right corner
|
|
572
|
+
|
|
530
573
|
### Creating Custom Plugins
|
|
531
574
|
|
|
532
575
|
```javascript
|
|
@@ -682,9 +682,22 @@ function extractColumnsFromSchema(schema) {
|
|
|
682
682
|
const shape = schema.shape;
|
|
683
683
|
|
|
684
684
|
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
685
|
+
let schemaToCheck = fieldSchema;
|
|
686
|
+
|
|
687
|
+
// Handle SchemaBuilder instances - finalize them first to get metadata
|
|
688
|
+
if (fieldSchema && typeof fieldSchema._finalize === 'function') {
|
|
689
|
+
schemaToCheck = fieldSchema._finalize();
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// Also check if it's a SchemaBuilder with _baseMeta (for unfinalized builders)
|
|
693
|
+
if (fieldSchema && fieldSchema._baseMeta) {
|
|
694
|
+
columns.set(key, fieldSchema._baseMeta);
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
|
|
685
698
|
// Unwrap optional/nullable wrappers to get to the base schema
|
|
686
|
-
let current =
|
|
687
|
-
while (current._def) {
|
|
699
|
+
let current = schemaToCheck;
|
|
700
|
+
while (current && current._def) {
|
|
688
701
|
if (current._def.innerType) {
|
|
689
702
|
current = current._def.innerType;
|
|
690
703
|
} else if (current._def.schema) {
|
|
@@ -694,8 +707,8 @@ function extractColumnsFromSchema(schema) {
|
|
|
694
707
|
}
|
|
695
708
|
}
|
|
696
709
|
|
|
697
|
-
// Check the
|
|
698
|
-
const meta = getColumnMeta(
|
|
710
|
+
// Check the schema for metadata
|
|
711
|
+
const meta = getColumnMeta(schemaToCheck) || (current ? getColumnMeta(current) : null);
|
|
699
712
|
if (meta) {
|
|
700
713
|
columns.set(key, meta);
|
|
701
714
|
}
|
package/package.json
CHANGED
package/plugins/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const analyticsPlugin = require('./analytics');
|
|
|
8
8
|
const dashboardPlugin = require('./dashboard/index');
|
|
9
9
|
const schemaExplorerPlugin = require('./schema-explorer');
|
|
10
10
|
const adminPanelPlugin = require('./admin-panel');
|
|
11
|
+
const seoCheckerPlugin = require('./seo-checker');
|
|
11
12
|
|
|
12
13
|
module.exports = {
|
|
13
14
|
sitemapPlugin,
|
|
@@ -15,5 +16,6 @@ module.exports = {
|
|
|
15
16
|
dashboardPlugin,
|
|
16
17
|
schemaExplorerPlugin,
|
|
17
18
|
adminPanelPlugin,
|
|
19
|
+
seoCheckerPlugin,
|
|
18
20
|
};
|
|
19
21
|
|