@jjlmoya/utils-motor 1.1.0

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.
Files changed (82) hide show
  1. package/package.json +68 -0
  2. package/scripts/postinstall.mjs +27 -0
  3. package/src/category/i18n/de.ts +17 -0
  4. package/src/category/i18n/en.ts +17 -0
  5. package/src/category/i18n/es.ts +17 -0
  6. package/src/category/i18n/fr.ts +17 -0
  7. package/src/category/i18n/id.ts +17 -0
  8. package/src/category/i18n/it.ts +17 -0
  9. package/src/category/i18n/ja.ts +17 -0
  10. package/src/category/i18n/ko.ts +17 -0
  11. package/src/category/i18n/nl.ts +17 -0
  12. package/src/category/i18n/pl.ts +17 -0
  13. package/src/category/i18n/pt.ts +17 -0
  14. package/src/category/i18n/ru.ts +17 -0
  15. package/src/category/i18n/sv.ts +17 -0
  16. package/src/category/i18n/tr.ts +17 -0
  17. package/src/category/i18n/zh.ts +17 -0
  18. package/src/category/index.ts +24 -0
  19. package/src/category/seo.astro +15 -0
  20. package/src/components/PreviewNavSidebar.astro +116 -0
  21. package/src/components/PreviewToolbar.astro +143 -0
  22. package/src/data.ts +11 -0
  23. package/src/entries.ts +7 -0
  24. package/src/env.d.ts +5 -0
  25. package/src/index.ts +23 -0
  26. package/src/layouts/PreviewLayout.astro +117 -0
  27. package/src/pages/[locale]/[slug].astro +153 -0
  28. package/src/pages/[locale].astro +251 -0
  29. package/src/pages/index.astro +4 -0
  30. package/src/tests/diacritics_density.test.ts +118 -0
  31. package/src/tests/faq_count.test.ts +19 -0
  32. package/src/tests/i18n_coverage.test.ts +36 -0
  33. package/src/tests/inverted_punctuation.test.ts +84 -0
  34. package/src/tests/locale_completeness.test.ts +29 -0
  35. package/src/tests/mocks/astro_mock.js +2 -0
  36. package/src/tests/no_en_dash.test.ts +70 -0
  37. package/src/tests/no_h1_in_components.test.ts +48 -0
  38. package/src/tests/pagespeed_best_practices.test.ts +198 -0
  39. package/src/tests/schemas_fulfillment.test.ts +23 -0
  40. package/src/tests/script_density.test.ts +94 -0
  41. package/src/tests/seo_length.test.ts +23 -0
  42. package/src/tests/seo_parity.test.ts +60 -0
  43. package/src/tests/shared-test-helpers.ts +56 -0
  44. package/src/tests/slug_language_code_format.test.ts +23 -0
  45. package/src/tests/slug_uniqueness.test.ts +81 -0
  46. package/src/tests/spanish_leakage.test.ts +177 -0
  47. package/src/tests/title_quality.test.ts +55 -0
  48. package/src/tests/tool_exports.test.ts +34 -0
  49. package/src/tests/tool_validation.test.ts +17 -0
  50. package/src/tests/translation_copy.test.ts +125 -0
  51. package/src/tool/brakingDistanceCalculator/bibliography.astro +6 -0
  52. package/src/tool/brakingDistanceCalculator/bibliography.ts +12 -0
  53. package/src/tool/brakingDistanceCalculator/braking-distance-calculator.css +601 -0
  54. package/src/tool/brakingDistanceCalculator/component.astro +117 -0
  55. package/src/tool/brakingDistanceCalculator/controller.ts +114 -0
  56. package/src/tool/brakingDistanceCalculator/dom-views.ts +58 -0
  57. package/src/tool/brakingDistanceCalculator/entry.ts +30 -0
  58. package/src/tool/brakingDistanceCalculator/evaluator.ts +45 -0
  59. package/src/tool/brakingDistanceCalculator/i18n/de.ts +45 -0
  60. package/src/tool/brakingDistanceCalculator/i18n/en.ts +208 -0
  61. package/src/tool/brakingDistanceCalculator/i18n/es.ts +46 -0
  62. package/src/tool/brakingDistanceCalculator/i18n/factory.ts +114 -0
  63. package/src/tool/brakingDistanceCalculator/i18n/fr.ts +40 -0
  64. package/src/tool/brakingDistanceCalculator/i18n/id.ts +38 -0
  65. package/src/tool/brakingDistanceCalculator/i18n/it.ts +39 -0
  66. package/src/tool/brakingDistanceCalculator/i18n/ja.ts +38 -0
  67. package/src/tool/brakingDistanceCalculator/i18n/ko.ts +38 -0
  68. package/src/tool/brakingDistanceCalculator/i18n/nl.ts +38 -0
  69. package/src/tool/brakingDistanceCalculator/i18n/pl.ts +38 -0
  70. package/src/tool/brakingDistanceCalculator/i18n/pt.ts +39 -0
  71. package/src/tool/brakingDistanceCalculator/i18n/ru.ts +38 -0
  72. package/src/tool/brakingDistanceCalculator/i18n/sv.ts +38 -0
  73. package/src/tool/brakingDistanceCalculator/i18n/tr.ts +38 -0
  74. package/src/tool/brakingDistanceCalculator/i18n/zh.ts +38 -0
  75. package/src/tool/brakingDistanceCalculator/index.ts +10 -0
  76. package/src/tool/brakingDistanceCalculator/logic.test.ts +84 -0
  77. package/src/tool/brakingDistanceCalculator/logic.ts +82 -0
  78. package/src/tool/brakingDistanceCalculator/seo.astro +15 -0
  79. package/src/tool/brakingDistanceCalculator/storage.ts +28 -0
  80. package/src/tool/brakingDistanceCalculator/ui.ts +42 -0
  81. package/src/tools.ts +4 -0
  82. package/src/types.ts +72 -0
@@ -0,0 +1,125 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { ALL_ENTRIES } from '../entries';
3
+
4
+ const COPY_THRESHOLD = 0.9;
5
+
6
+ const STRUCTURAL_KEYS = new Set([
7
+ '@context',
8
+ '@type',
9
+ 'applicationCategory',
10
+ 'columns',
11
+ 'highlight',
12
+ 'icon',
13
+ 'level',
14
+ 'operatingSystem',
15
+ 'position',
16
+ 'positive',
17
+ 'price',
18
+ 'priceCurrency',
19
+ 'slug',
20
+ 'trend',
21
+ 'type',
22
+ 'url',
23
+ 'value',
24
+ 'variant',
25
+ ]);
26
+
27
+ function normalizeText(value: string): string {
28
+ return value
29
+ .replace(/<[^>]*>/g, ' ')
30
+ .replace(/&(?:amp|lt|gt|quot|apos|nbsp);/gi, ' ')
31
+ .replace(/[\u2018\u2019]/g, "'")
32
+ .replace(/[\u201c\u201d]/g, '"')
33
+ .replace(/\s+/g, ' ')
34
+ .trim()
35
+ .toLocaleLowerCase();
36
+ }
37
+
38
+ function collectText(value: unknown, path: string, parts: string[]): void {
39
+ if (typeof value === 'string') {
40
+ const normalized = normalizeText(value);
41
+ if (normalized.length >= 2) parts.push(normalized);
42
+ return;
43
+ }
44
+
45
+ if (Array.isArray(value)) {
46
+ value.forEach((item, index) => collectText(item, `${path}[${index}]`, parts));
47
+ return;
48
+ }
49
+
50
+ if (!value || typeof value !== 'object') return;
51
+
52
+ Object.entries(value).forEach(([key, child]) => {
53
+ if (STRUCTURAL_KEYS.has(key)) return;
54
+ collectText(child, `${path}.${key}`, parts);
55
+ });
56
+ }
57
+
58
+ function localeCorpus(content: unknown): string {
59
+ if (!content || typeof content !== 'object') return '';
60
+
61
+ const record = content as Record<string, unknown>;
62
+ const parts: string[] = [];
63
+ collectText(record.title, 'title', parts);
64
+ collectText(record.description, 'description', parts);
65
+ collectText(record.faqTitle, 'faqTitle', parts);
66
+ collectText(record.faq, 'faq', parts);
67
+ collectText(record.seo, 'seo', parts);
68
+ collectText(record.schemas, 'schemas', parts);
69
+ return parts.join(' ');
70
+ }
71
+
72
+ function tokenCounts(text: string): Map<string, number> {
73
+ const counts = new Map<string, number>();
74
+ for (const token of text.match(/[\p{L}\p{N}]+/gu) ?? []) {
75
+ counts.set(token, (counts.get(token) ?? 0) + 1);
76
+ }
77
+ return counts;
78
+ }
79
+
80
+ function copySimilarity(left: string, right: string): number {
81
+ const leftCounts = tokenCounts(left);
82
+ const rightCounts = tokenCounts(right);
83
+ const leftTotal = [...leftCounts.values()].reduce((sum, count) => sum + count, 0);
84
+ const rightTotal = [...rightCounts.values()].reduce((sum, count) => sum + count, 0);
85
+ if (leftTotal === 0 || rightTotal === 0) return 0;
86
+
87
+ let shared = 0;
88
+ for (const [token, count] of leftCounts) {
89
+ shared += Math.min(count, rightCounts.get(token) ?? 0);
90
+ }
91
+ return (2 * shared) / (leftTotal + rightTotal);
92
+ }
93
+
94
+ function findCopyViolations(corpora: Map<string, string>): string[] {
95
+ const locales = [...corpora.keys()];
96
+ const violations: string[] = [];
97
+ for (let leftIndex = 0; leftIndex < locales.length; leftIndex += 1) {
98
+ for (let rightIndex = leftIndex + 1; rightIndex < locales.length; rightIndex += 1) {
99
+ const left = locales[leftIndex] ?? '';
100
+ const right = locales[rightIndex] ?? '';
101
+ const similarity = copySimilarity(corpora.get(left) ?? '', corpora.get(right) ?? '');
102
+ if (similarity >= COPY_THRESHOLD) {
103
+ violations.push(`${left} ↔ ${right}: ${(similarity * 100).toFixed(1)}%`);
104
+ }
105
+ }
106
+ }
107
+ return violations;
108
+ }
109
+
110
+ describe('Locales must not copy another locale wholesale', () => {
111
+ ALL_ENTRIES.forEach((entry) => {
112
+ it(`${entry.id} is not at least ${COPY_THRESHOLD * 100}% identical to another locale`, async () => {
113
+ const corpora = new Map<string, string>();
114
+
115
+ for (const [locale, loader] of Object.entries(entry.i18n)) {
116
+ if (!loader) continue;
117
+ corpora.set(locale, localeCorpus(await loader()));
118
+ }
119
+
120
+ const violations = findCopyViolations(corpora);
121
+
122
+ expect(violations, `Locale copy threshold exceeded in ${entry.id}`).toEqual([]);
123
+ });
124
+ });
125
+ });
@@ -0,0 +1,6 @@
1
+ ---
2
+ import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
3
+ import { bibliographyEntries } from './bibliography';
4
+ ---
5
+
6
+ <SharedBibliography links={bibliographyEntries} />
@@ -0,0 +1,12 @@
1
+ import type { BibliographyEntry } from '../../types';
2
+
3
+ export const bibliographyEntries: BibliographyEntry[] = [
4
+ {
5
+ name: 'Federal Highway Administration stopping sight distance model',
6
+ url: 'https://www.fhwa.dot.gov/publications/research/safety/ihsdm/02045/3.cfm',
7
+ },
8
+ {
9
+ name: 'UK Highway Code rule 126 stopping distances',
10
+ url: 'https://www.gov.uk/guidance/the-highway-code/general-rules-techniques-and-advice-for-all-drivers-and-riders-103-to-158',
11
+ },
12
+ ];