stylemcp 0.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.
- package/README.md +261 -0
- package/action/action.yml +59 -0
- package/action/package.json +24 -0
- package/action/src/index.ts +455 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +335 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/rewriter/index.d.ts +45 -0
- package/dist/rewriter/index.d.ts.map +1 -0
- package/dist/rewriter/index.js +163 -0
- package/dist/rewriter/index.js.map +1 -0
- package/dist/schema/copy-patterns.d.ts +298 -0
- package/dist/schema/copy-patterns.d.ts.map +1 -0
- package/dist/schema/copy-patterns.js +53 -0
- package/dist/schema/copy-patterns.js.map +1 -0
- package/dist/schema/cta-rules.d.ts +274 -0
- package/dist/schema/cta-rules.d.ts.map +1 -0
- package/dist/schema/cta-rules.js +45 -0
- package/dist/schema/cta-rules.js.map +1 -0
- package/dist/schema/index.d.ts +2172 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +92 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/tests.d.ts +274 -0
- package/dist/schema/tests.d.ts.map +1 -0
- package/dist/schema/tests.js +37 -0
- package/dist/schema/tests.js.map +1 -0
- package/dist/schema/tokens.d.ts +632 -0
- package/dist/schema/tokens.d.ts.map +1 -0
- package/dist/schema/tokens.js +68 -0
- package/dist/schema/tokens.js.map +1 -0
- package/dist/schema/voice.d.ts +280 -0
- package/dist/schema/voice.d.ts.map +1 -0
- package/dist/schema/voice.js +52 -0
- package/dist/schema/voice.js.map +1 -0
- package/dist/server/billing.d.ts +53 -0
- package/dist/server/billing.d.ts.map +1 -0
- package/dist/server/billing.js +216 -0
- package/dist/server/billing.js.map +1 -0
- package/dist/server/http.d.ts +5 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +470 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +480 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/middleware/auth.d.ts +15 -0
- package/dist/server/middleware/auth.d.ts.map +1 -0
- package/dist/server/middleware/auth.js +83 -0
- package/dist/server/middleware/auth.js.map +1 -0
- package/dist/utils/pack-loader.d.ts +13 -0
- package/dist/utils/pack-loader.d.ts.map +1 -0
- package/dist/utils/pack-loader.js +98 -0
- package/dist/utils/pack-loader.js.map +1 -0
- package/dist/validator/index.d.ts +21 -0
- package/dist/validator/index.d.ts.map +1 -0
- package/dist/validator/index.js +60 -0
- package/dist/validator/index.js.map +1 -0
- package/dist/validator/rules/constraints.d.ts +8 -0
- package/dist/validator/rules/constraints.d.ts.map +1 -0
- package/dist/validator/rules/constraints.js +150 -0
- package/dist/validator/rules/constraints.js.map +1 -0
- package/dist/validator/rules/cta.d.ts +11 -0
- package/dist/validator/rules/cta.d.ts.map +1 -0
- package/dist/validator/rules/cta.js +113 -0
- package/dist/validator/rules/cta.js.map +1 -0
- package/dist/validator/rules/voice.d.ts +6 -0
- package/dist/validator/rules/voice.d.ts.map +1 -0
- package/dist/validator/rules/voice.js +106 -0
- package/dist/validator/rules/voice.js.map +1 -0
- package/package.json +61 -0
- package/packs/saas/copy_patterns.yaml +423 -0
- package/packs/saas/cta_rules.yaml +362 -0
- package/packs/saas/manifest.yaml +16 -0
- package/packs/saas/tests.yaml +305 -0
- package/packs/saas/tokens.json +226 -0
- package/packs/saas/voice.yaml +232 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import * as yaml from 'js-yaml';
|
|
5
|
+
import { PackManifestSchema, VoiceSchema, CopyPatternsSchema, CTARulesSchema, TokensSchema, TestSuiteSchema, } from '../schema/index.js';
|
|
6
|
+
async function loadYamlFile(filePath, schema) {
|
|
7
|
+
const content = await readFile(filePath, 'utf-8');
|
|
8
|
+
const data = yaml.load(content);
|
|
9
|
+
return schema.parse(data);
|
|
10
|
+
}
|
|
11
|
+
async function loadJsonFile(filePath, schema) {
|
|
12
|
+
const content = await readFile(filePath, 'utf-8');
|
|
13
|
+
const data = JSON.parse(content);
|
|
14
|
+
return schema.parse(data);
|
|
15
|
+
}
|
|
16
|
+
export async function loadPack(options) {
|
|
17
|
+
const { packPath, validate = true } = options;
|
|
18
|
+
const errors = [];
|
|
19
|
+
// Load manifest
|
|
20
|
+
const manifestPath = join(packPath, 'manifest.yaml');
|
|
21
|
+
let manifest;
|
|
22
|
+
try {
|
|
23
|
+
manifest = await loadYamlFile(manifestPath, PackManifestSchema);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
throw new Error(`Failed to load manifest: ${err instanceof Error ? err.message : err}`);
|
|
27
|
+
}
|
|
28
|
+
const files = manifest.files;
|
|
29
|
+
// Load each component
|
|
30
|
+
let voice;
|
|
31
|
+
let copyPatterns;
|
|
32
|
+
let ctaRules;
|
|
33
|
+
let tokens;
|
|
34
|
+
let tests;
|
|
35
|
+
try {
|
|
36
|
+
voice = await loadYamlFile(join(packPath, files.voice), VoiceSchema);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
errors.push(`Failed to load voice: ${err instanceof Error ? err.message : err}`);
|
|
40
|
+
voice = VoiceSchema.parse({ name: 'default', tone: { attributes: [] }, vocabulary: { rules: [] } });
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
copyPatterns = await loadYamlFile(join(packPath, files.copyPatterns), CopyPatternsSchema);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
errors.push(`Failed to load copy patterns: ${err instanceof Error ? err.message : err}`);
|
|
47
|
+
copyPatterns = CopyPatternsSchema.parse({ name: 'default', patterns: [] });
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
ctaRules = await loadYamlFile(join(packPath, files.ctaRules), CTARulesSchema);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
errors.push(`Failed to load CTA rules: ${err instanceof Error ? err.message : err}`);
|
|
54
|
+
ctaRules = CTARulesSchema.parse({ name: 'default', categories: [] });
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
tokens = await loadJsonFile(join(packPath, files.tokens), TokensSchema);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
errors.push(`Failed to load tokens: ${err instanceof Error ? err.message : err}`);
|
|
61
|
+
tokens = TokensSchema.parse({ name: 'default' });
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
tests = await loadYamlFile(join(packPath, files.tests), TestSuiteSchema);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
errors.push(`Failed to load tests: ${err instanceof Error ? err.message : err}`);
|
|
68
|
+
tests = TestSuiteSchema.parse({ name: 'default', tests: [] });
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
pack: {
|
|
72
|
+
manifest,
|
|
73
|
+
voice,
|
|
74
|
+
copyPatterns,
|
|
75
|
+
ctaRules,
|
|
76
|
+
tokens,
|
|
77
|
+
tests,
|
|
78
|
+
},
|
|
79
|
+
errors,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function getPacksDirectory() {
|
|
83
|
+
// Get the packs directory relative to this file
|
|
84
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
85
|
+
return join(currentDir, '..', '..', 'packs');
|
|
86
|
+
}
|
|
87
|
+
export async function listAvailablePacks() {
|
|
88
|
+
const { readdir } = await import('fs/promises');
|
|
89
|
+
const packsDir = getPacksDirectory();
|
|
90
|
+
try {
|
|
91
|
+
const entries = await readdir(packsDir, { withFileTypes: true });
|
|
92
|
+
return entries.filter(e => e.isDirectory()).map(e => e.name);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=pack-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack-loader.js","sourceRoot":"","sources":["../../src/utils/pack-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,KAAK,IAAI,MAAM,SAAS,CAAC;AAChC,OAAO,EAGL,kBAAkB,EAClB,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,eAAe,GAMhB,MAAM,oBAAoB,CAAC;AAY5B,KAAK,UAAU,YAAY,CAAI,QAAgB,EAAE,MAAuC;IACtF,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChC,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,KAAK,UAAU,YAAY,CAAI,QAAgB,EAAE,MAAuC;IACtF,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjC,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAwB;IACrD,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAC9C,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,gBAAgB;IAChB,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;IACrD,IAAI,QAAsB,CAAC;IAC3B,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,YAAY,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC;IAClE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IAC1F,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;IAE7B,sBAAsB;IACtB,IAAI,KAAY,CAAC;IACjB,IAAI,YAA0B,CAAC;IAC/B,IAAI,QAAkB,CAAC;IACvB,IAAI,MAAc,CAAC;IACnB,IAAI,KAAgB,CAAC;IAErB,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,CAAC;IACvE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,yBAAyB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACjF,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IACtG,CAAC;IAED,IAAI,CAAC;QACH,YAAY,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC,EAAE,kBAAkB,CAAC,CAAC;IAC5F,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,iCAAiC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACzF,YAAY,GAAG,kBAAkB,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC,CAAC;IAChF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,6BAA6B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACrF,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC;IAC1E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,0BAA0B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAClF,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC,CAAC;IAC3E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,yBAAyB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACjF,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,OAAO;QACL,IAAI,EAAE;YACJ,QAAQ;YACR,KAAK;YACL,YAAY;YACZ,QAAQ;YACR,MAAM;YACN,KAAK;SACN;QACD,MAAM;KACP,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,gDAAgD;IAChD,MAAM,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3D,OAAO,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACjE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Pack, ValidationResult } from '../schema/index.js';
|
|
2
|
+
export interface ValidateOptions {
|
|
3
|
+
pack: Pack;
|
|
4
|
+
text: string;
|
|
5
|
+
context?: {
|
|
6
|
+
type?: 'ui-copy' | 'marketing' | 'docs' | 'support' | 'general';
|
|
7
|
+
component?: string;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export interface ValidatorConfig {
|
|
11
|
+
strictMode?: boolean;
|
|
12
|
+
minScore?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Main validation function
|
|
16
|
+
*/
|
|
17
|
+
export declare function validate(options: ValidateOptions): ValidationResult;
|
|
18
|
+
export { checkVoiceRules } from './rules/voice.js';
|
|
19
|
+
export { checkCtaRules } from './rules/cta.js';
|
|
20
|
+
export { checkConstraints } from './rules/constraints.js';
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/validator/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAa,MAAM,oBAAoB,CAAC;AAKvE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;QAChE,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,gBAAgB,CAqCnE;AAsBD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { checkVoiceRules } from './rules/voice.js';
|
|
2
|
+
import { checkCtaRules } from './rules/cta.js';
|
|
3
|
+
import { checkConstraints } from './rules/constraints.js';
|
|
4
|
+
/**
|
|
5
|
+
* Main validation function
|
|
6
|
+
*/
|
|
7
|
+
export function validate(options) {
|
|
8
|
+
const { pack, text, context } = options;
|
|
9
|
+
const violations = [];
|
|
10
|
+
// Run all rule checkers
|
|
11
|
+
violations.push(...checkVoiceRules(text, pack.voice));
|
|
12
|
+
violations.push(...checkCtaRules(text, pack.ctaRules, context));
|
|
13
|
+
violations.push(...checkConstraints(text, pack.voice.constraints));
|
|
14
|
+
// Calculate score
|
|
15
|
+
const score = calculateScore(violations);
|
|
16
|
+
// Determine if valid based on pack config
|
|
17
|
+
const minScore = pack.manifest.config?.minScore ?? 70;
|
|
18
|
+
const strictMode = pack.manifest.config?.strictMode ?? false;
|
|
19
|
+
const hasErrors = violations.some(v => v.severity === 'error');
|
|
20
|
+
const valid = strictMode
|
|
21
|
+
? violations.length === 0
|
|
22
|
+
: score >= minScore && !hasErrors;
|
|
23
|
+
return {
|
|
24
|
+
valid,
|
|
25
|
+
score,
|
|
26
|
+
input: text,
|
|
27
|
+
violations,
|
|
28
|
+
summary: {
|
|
29
|
+
errors: violations.filter(v => v.severity === 'error').length,
|
|
30
|
+
warnings: violations.filter(v => v.severity === 'warning').length,
|
|
31
|
+
info: violations.filter(v => v.severity === 'info').length,
|
|
32
|
+
},
|
|
33
|
+
metadata: {
|
|
34
|
+
packName: pack.manifest.name,
|
|
35
|
+
packVersion: pack.manifest.version,
|
|
36
|
+
validatedAt: new Date().toISOString(),
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Calculate a 0-100 score based on violations
|
|
42
|
+
*/
|
|
43
|
+
function calculateScore(violations) {
|
|
44
|
+
if (violations.length === 0)
|
|
45
|
+
return 100;
|
|
46
|
+
// Weight by severity
|
|
47
|
+
const weights = {
|
|
48
|
+
error: 25,
|
|
49
|
+
warning: 10,
|
|
50
|
+
info: 3,
|
|
51
|
+
};
|
|
52
|
+
const totalPenalty = violations.reduce((sum, v) => sum + weights[v.severity], 0);
|
|
53
|
+
// Cap penalty at 100
|
|
54
|
+
const score = Math.max(0, 100 - totalPenalty);
|
|
55
|
+
return Math.round(score);
|
|
56
|
+
}
|
|
57
|
+
export { checkVoiceRules } from './rules/voice.js';
|
|
58
|
+
export { checkCtaRules } from './rules/cta.js';
|
|
59
|
+
export { checkConstraints } from './rules/constraints.js';
|
|
60
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/validator/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Voice, Violation } from '../../schema/index.js';
|
|
2
|
+
type Constraints = Voice['constraints'];
|
|
3
|
+
/**
|
|
4
|
+
* Check text against writing constraints
|
|
5
|
+
*/
|
|
6
|
+
export declare function checkConstraints(text: string, constraints: Constraints): Violation[];
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=constraints.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constraints.d.ts","sourceRoot":"","sources":["../../../src/validator/rules/constraints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAuBzD,KAAK,WAAW,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC;AAExC;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,SAAS,EAAE,CA0BpF"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
let violationId = 2000;
|
|
2
|
+
function createViolation(rule, severity, message, text, position, suggestion) {
|
|
3
|
+
return {
|
|
4
|
+
id: `v-${++violationId}`,
|
|
5
|
+
rule,
|
|
6
|
+
severity,
|
|
7
|
+
message,
|
|
8
|
+
text,
|
|
9
|
+
position,
|
|
10
|
+
suggestion,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Check text against writing constraints
|
|
15
|
+
*/
|
|
16
|
+
export function checkConstraints(text, constraints) {
|
|
17
|
+
const violations = [];
|
|
18
|
+
if (!constraints)
|
|
19
|
+
return violations;
|
|
20
|
+
// Check sentence length
|
|
21
|
+
if (constraints.maxSentenceLength) {
|
|
22
|
+
violations.push(...checkSentenceLength(text, constraints.maxSentenceLength));
|
|
23
|
+
}
|
|
24
|
+
// Check paragraph length
|
|
25
|
+
if (constraints.maxParagraphLength) {
|
|
26
|
+
violations.push(...checkParagraphLength(text, constraints.maxParagraphLength));
|
|
27
|
+
}
|
|
28
|
+
// Check contractions
|
|
29
|
+
if (constraints.contractions) {
|
|
30
|
+
violations.push(...checkContractions(text, constraints.contractions));
|
|
31
|
+
}
|
|
32
|
+
// Check Oxford comma
|
|
33
|
+
if (constraints.oxfordComma !== undefined) {
|
|
34
|
+
violations.push(...checkOxfordComma(text, constraints.oxfordComma));
|
|
35
|
+
}
|
|
36
|
+
return violations;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check for overly long sentences
|
|
40
|
+
*/
|
|
41
|
+
function checkSentenceLength(text, maxWords) {
|
|
42
|
+
const violations = [];
|
|
43
|
+
// Split into sentences (simple approach)
|
|
44
|
+
const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0);
|
|
45
|
+
let position = 0;
|
|
46
|
+
for (const sentence of sentences) {
|
|
47
|
+
const words = sentence.trim().split(/\s+/).filter(w => w.length > 0);
|
|
48
|
+
if (words.length > maxWords) {
|
|
49
|
+
const start = text.indexOf(sentence.trim(), position);
|
|
50
|
+
const end = start + sentence.trim().length;
|
|
51
|
+
violations.push(createViolation('constraints.maxSentenceLength', 'warning', `Sentence has ${words.length} words, max is ${maxWords}`, sentence.trim(), { start, end }, 'Break into shorter sentences'));
|
|
52
|
+
}
|
|
53
|
+
position = text.indexOf(sentence, position) + sentence.length;
|
|
54
|
+
}
|
|
55
|
+
return violations;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Check for overly long paragraphs
|
|
59
|
+
*/
|
|
60
|
+
function checkParagraphLength(text, maxSentences) {
|
|
61
|
+
const violations = [];
|
|
62
|
+
// Split into paragraphs
|
|
63
|
+
const paragraphs = text.split(/\n\n+/).filter(p => p.trim().length > 0);
|
|
64
|
+
let position = 0;
|
|
65
|
+
for (const paragraph of paragraphs) {
|
|
66
|
+
const sentences = paragraph.split(/[.!?]+/).filter(s => s.trim().length > 0);
|
|
67
|
+
if (sentences.length > maxSentences) {
|
|
68
|
+
const start = text.indexOf(paragraph, position);
|
|
69
|
+
const end = start + paragraph.length;
|
|
70
|
+
violations.push(createViolation('constraints.maxParagraphLength', 'info', `Paragraph has ${sentences.length} sentences, max is ${maxSentences}`, paragraph.slice(0, 50) + '...', { start, end }, 'Break into shorter paragraphs'));
|
|
71
|
+
}
|
|
72
|
+
position = text.indexOf(paragraph, position) + paragraph.length;
|
|
73
|
+
}
|
|
74
|
+
return violations;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check contraction usage
|
|
78
|
+
*/
|
|
79
|
+
function checkContractions(text, rule) {
|
|
80
|
+
const violations = [];
|
|
81
|
+
// Common contractions
|
|
82
|
+
const contractions = [
|
|
83
|
+
"don't", "doesn't", "didn't", "can't", "couldn't", "won't", "wouldn't",
|
|
84
|
+
"shouldn't", "isn't", "aren't", "wasn't", "weren't", "hasn't", "haven't",
|
|
85
|
+
"hadn't", "it's", "that's", "what's", "who's", "there's", "here's",
|
|
86
|
+
"let's", "I'm", "you're", "we're", "they're", "he's", "she's",
|
|
87
|
+
"I'll", "you'll", "we'll", "they'll", "he'll", "she'll", "it'll",
|
|
88
|
+
"I've", "you've", "we've", "they've", "I'd", "you'd", "we'd", "they'd"
|
|
89
|
+
];
|
|
90
|
+
// Expanded forms
|
|
91
|
+
const expanded = [
|
|
92
|
+
"do not", "does not", "did not", "cannot", "could not", "will not", "would not",
|
|
93
|
+
"should not", "is not", "are not", "was not", "were not", "has not", "have not",
|
|
94
|
+
"had not", "it is", "that is", "what is", "who is", "there is", "here is",
|
|
95
|
+
"let us", "I am", "you are", "we are", "they are", "he is", "she is",
|
|
96
|
+
"I will", "you will", "we will", "they will", "he will", "she will", "it will",
|
|
97
|
+
"I have", "you have", "we have", "they have", "I would", "you would", "we would", "they would"
|
|
98
|
+
];
|
|
99
|
+
if (rule === 'forbidden') {
|
|
100
|
+
// Check for contractions
|
|
101
|
+
for (const contraction of contractions) {
|
|
102
|
+
const regex = new RegExp(`\\b${escapeRegex(contraction)}\\b`, 'gi');
|
|
103
|
+
let match;
|
|
104
|
+
while ((match = regex.exec(text)) !== null) {
|
|
105
|
+
const idx = contractions.indexOf(contraction.toLowerCase());
|
|
106
|
+
violations.push(createViolation('constraints.contractions', 'warning', `Contractions are not allowed`, match[0], { start: match.index, end: match.index + match[0].length }, idx >= 0 ? expanded[idx] : 'Expand the contraction'));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else if (rule === 'required') {
|
|
111
|
+
// Check for expanded forms that should be contracted
|
|
112
|
+
for (let i = 0; i < expanded.length; i++) {
|
|
113
|
+
const exp = expanded[i];
|
|
114
|
+
const regex = new RegExp(`\\b${escapeRegex(exp)}\\b`, 'gi');
|
|
115
|
+
let match;
|
|
116
|
+
while ((match = regex.exec(text)) !== null) {
|
|
117
|
+
violations.push(createViolation('constraints.contractions', 'info', `Use contractions for a more natural tone`, match[0], { start: match.index, end: match.index + match[0].length }, contractions[i]));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return violations;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Check Oxford comma usage
|
|
125
|
+
*/
|
|
126
|
+
function checkOxfordComma(text, required) {
|
|
127
|
+
const violations = [];
|
|
128
|
+
// Pattern for lists: "X, Y and Z" or "X, Y, and Z"
|
|
129
|
+
// With Oxford comma: item, item, and item
|
|
130
|
+
// Without Oxford comma: item, item and item
|
|
131
|
+
if (required) {
|
|
132
|
+
// Look for missing Oxford comma: "X, Y and Z"
|
|
133
|
+
const missingOxfordRegex = /(\w+),\s+(\w+)\s+and\s+(\w+)/gi;
|
|
134
|
+
let match;
|
|
135
|
+
while ((match = missingOxfordRegex.exec(text)) !== null) {
|
|
136
|
+
// Check if there's no comma before "and"
|
|
137
|
+
const beforeAnd = match[0].lastIndexOf(',');
|
|
138
|
+
const andPos = match[0].toLowerCase().lastIndexOf(' and ');
|
|
139
|
+
if (beforeAnd < andPos - 5) {
|
|
140
|
+
// Likely missing Oxford comma
|
|
141
|
+
violations.push(createViolation('constraints.oxfordComma', 'info', 'Use the Oxford comma before "and" in lists', match[0], { start: match.index, end: match.index + match[0].length }, `${match[1]}, ${match[2]}, and ${match[3]}`));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return violations;
|
|
146
|
+
}
|
|
147
|
+
function escapeRegex(str) {
|
|
148
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=constraints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constraints.js","sourceRoot":"","sources":["../../../src/validator/rules/constraints.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CTARules, Violation } from '../../schema/index.js';
|
|
2
|
+
export interface CtaContext {
|
|
3
|
+
type?: 'ui-copy' | 'marketing' | 'docs' | 'support' | 'general';
|
|
4
|
+
component?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Check text against CTA rules
|
|
8
|
+
* Note: This primarily checks short text that appears to be CTAs (buttons, links)
|
|
9
|
+
*/
|
|
10
|
+
export declare function checkCtaRules(text: string, ctaRules: CTARules, context?: CtaContext): Violation[];
|
|
11
|
+
//# sourceMappingURL=cta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cta.d.ts","sourceRoot":"","sources":["../../../src/validator/rules/cta.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAuB5D,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,UAAU,GACnB,SAAS,EAAE,CAsBb"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
let violationId = 1000;
|
|
2
|
+
function createViolation(rule, severity, message, text, position, suggestion) {
|
|
3
|
+
return {
|
|
4
|
+
id: `v-${++violationId}`,
|
|
5
|
+
rule,
|
|
6
|
+
severity,
|
|
7
|
+
message,
|
|
8
|
+
text,
|
|
9
|
+
position,
|
|
10
|
+
suggestion,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Check text against CTA rules
|
|
15
|
+
* Note: This primarily checks short text that appears to be CTAs (buttons, links)
|
|
16
|
+
*/
|
|
17
|
+
export function checkCtaRules(text, ctaRules, context) {
|
|
18
|
+
const violations = [];
|
|
19
|
+
// Only apply CTA rules to short text (likely buttons/links)
|
|
20
|
+
// Longer text should only check for anti-patterns like "click here"
|
|
21
|
+
const isShortText = text.split(/\s+/).length <= 6;
|
|
22
|
+
const isButton = context?.component === 'button';
|
|
23
|
+
// Check anti-patterns (applies to all text)
|
|
24
|
+
violations.push(...checkCtaAntiPatterns(text, ctaRules.antiPatterns));
|
|
25
|
+
// Check guidelines only for button-like text
|
|
26
|
+
if (isShortText || isButton) {
|
|
27
|
+
violations.push(...checkCtaGuidelines(text, ctaRules.guidelines));
|
|
28
|
+
}
|
|
29
|
+
// Check contextual rules if we have context
|
|
30
|
+
if (context) {
|
|
31
|
+
violations.push(...checkContextualRules(text, ctaRules.contextualRules, context));
|
|
32
|
+
}
|
|
33
|
+
return violations;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Check for CTA anti-patterns
|
|
37
|
+
*/
|
|
38
|
+
function checkCtaAntiPatterns(text, antiPatterns) {
|
|
39
|
+
const violations = [];
|
|
40
|
+
for (const pattern of antiPatterns) {
|
|
41
|
+
let regex;
|
|
42
|
+
try {
|
|
43
|
+
if (pattern.isRegex) {
|
|
44
|
+
regex = new RegExp(pattern.pattern, 'gi');
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
regex = new RegExp(`^${escapeRegex(pattern.pattern)}$`, 'i');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const match = regex.exec(text);
|
|
54
|
+
if (match) {
|
|
55
|
+
violations.push(createViolation('cta.antiPattern', 'warning', pattern.reason, match[0], { start: match.index, end: match.index + match[0].length }, pattern.suggestion));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return violations;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check CTA guidelines
|
|
62
|
+
*/
|
|
63
|
+
function checkCtaGuidelines(text, guidelines) {
|
|
64
|
+
const violations = [];
|
|
65
|
+
// Check max words
|
|
66
|
+
const wordCount = text.split(/\s+/).filter(w => w.length > 0).length;
|
|
67
|
+
if (guidelines.maxWords && wordCount > guidelines.maxWords) {
|
|
68
|
+
violations.push(createViolation('cta.maxWords', 'info', `CTA has ${wordCount} words, max is ${guidelines.maxWords}`, text, { start: 0, end: text.length }, 'Shorten to be more direct'));
|
|
69
|
+
}
|
|
70
|
+
// Check for avoided words
|
|
71
|
+
for (const avoidWord of guidelines.avoidWords) {
|
|
72
|
+
const regex = new RegExp(`\\b${escapeRegex(avoidWord)}\\b`, 'gi');
|
|
73
|
+
const match = regex.exec(text);
|
|
74
|
+
if (match) {
|
|
75
|
+
violations.push(createViolation('cta.avoidWord', 'warning', `Avoid "${avoidWord}" in CTAs`, match[0], { start: match.index, end: match.index + match[0].length }, `Use a more specific action verb`));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Check capitalization
|
|
79
|
+
if (guidelines.capitalization === 'sentence' && text.length > 1) {
|
|
80
|
+
const words = text.split(/\s+/);
|
|
81
|
+
// First word should be capitalized, rest should be lowercase (unless proper nouns)
|
|
82
|
+
const firstWord = words[0];
|
|
83
|
+
if (firstWord && firstWord[0] !== firstWord[0].toUpperCase()) {
|
|
84
|
+
violations.push(createViolation('cta.capitalization', 'info', 'CTA should use sentence case (capitalize first letter)', text, { start: 0, end: text.length }));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return violations;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Check contextual CTA rules
|
|
91
|
+
*/
|
|
92
|
+
function checkContextualRules(text, contextualRules, context) {
|
|
93
|
+
const violations = [];
|
|
94
|
+
const lowerText = text.toLowerCase();
|
|
95
|
+
for (const rule of contextualRules) {
|
|
96
|
+
// Simple context matching
|
|
97
|
+
const contextMatches = rule.context.toLowerCase().includes(context.type || '') ||
|
|
98
|
+
rule.context.toLowerCase().includes(context.component || '');
|
|
99
|
+
if (!contextMatches)
|
|
100
|
+
continue;
|
|
101
|
+
// Check forbidden in context
|
|
102
|
+
for (const forbidden of rule.forbidden) {
|
|
103
|
+
if (lowerText === forbidden.toLowerCase()) {
|
|
104
|
+
violations.push(createViolation('cta.contextForbidden', 'warning', `"${text}" should not be used in ${rule.context}`, text, { start: 0, end: text.length }, rule.preferred.length > 0 ? `Try: ${rule.preferred.join(', ')}` : undefined));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return violations;
|
|
109
|
+
}
|
|
110
|
+
function escapeRegex(str) {
|
|
111
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=cta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cta.js","sourceRoot":"","sources":["../../../src/validator/rules/cta.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voice.d.ts","sourceRoot":"","sources":["../../../src/validator/rules/voice.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAuBzD;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,SAAS,EAAE,CAavE"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
let violationId = 0;
|
|
2
|
+
function createViolation(rule, severity, message, text, position, suggestion) {
|
|
3
|
+
return {
|
|
4
|
+
id: `v-${++violationId}`,
|
|
5
|
+
rule,
|
|
6
|
+
severity,
|
|
7
|
+
message,
|
|
8
|
+
text,
|
|
9
|
+
position,
|
|
10
|
+
suggestion,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Check text against voice rules (vocabulary, doNot, forbidden phrases)
|
|
15
|
+
*/
|
|
16
|
+
export function checkVoiceRules(text, voice) {
|
|
17
|
+
const violations = [];
|
|
18
|
+
// Check forbidden words
|
|
19
|
+
violations.push(...checkForbiddenWords(text, voice.vocabulary.forbidden));
|
|
20
|
+
// Check vocabulary preferences
|
|
21
|
+
violations.push(...checkVocabularyRules(text, voice.vocabulary.rules));
|
|
22
|
+
// Check doNot patterns
|
|
23
|
+
violations.push(...checkDoNotPatterns(text, voice.doNot));
|
|
24
|
+
return violations;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Check for forbidden words/phrases
|
|
28
|
+
*/
|
|
29
|
+
function checkForbiddenWords(text, forbidden) {
|
|
30
|
+
const violations = [];
|
|
31
|
+
const lowerText = text.toLowerCase();
|
|
32
|
+
for (const word of forbidden) {
|
|
33
|
+
const lowerWord = word.toLowerCase();
|
|
34
|
+
// Use word boundary matching
|
|
35
|
+
const regex = new RegExp(`\\b${escapeRegex(lowerWord)}\\b`, 'gi');
|
|
36
|
+
let match;
|
|
37
|
+
while ((match = regex.exec(text)) !== null) {
|
|
38
|
+
violations.push(createViolation('vocabulary.forbidden', 'error', `Forbidden phrase: "${word}"`, match[0], { start: match.index, end: match.index + match[0].length }, `Remove or replace "${word}"`));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return violations;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check vocabulary preference rules (use X instead of Y)
|
|
45
|
+
*/
|
|
46
|
+
function checkVocabularyRules(text, rules) {
|
|
47
|
+
const violations = [];
|
|
48
|
+
for (const rule of rules) {
|
|
49
|
+
for (const avoid of rule.avoid) {
|
|
50
|
+
const regex = new RegExp(`\\b${escapeRegex(avoid)}\\b`, 'gi');
|
|
51
|
+
let match;
|
|
52
|
+
while ((match = regex.exec(text)) !== null) {
|
|
53
|
+
violations.push(createViolation('vocabulary.preferred', 'warning', `Use "${rule.preferred}" instead of "${avoid}"`, match[0], { start: match.index, end: match.index + match[0].length }, rule.preferred));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return violations;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Check doNot patterns
|
|
61
|
+
*/
|
|
62
|
+
function checkDoNotPatterns(text, doNots) {
|
|
63
|
+
const violations = [];
|
|
64
|
+
for (const doNot of doNots) {
|
|
65
|
+
let regex;
|
|
66
|
+
try {
|
|
67
|
+
if (doNot.isRegex) {
|
|
68
|
+
regex = new RegExp(doNot.pattern, 'gi');
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// Exact string match with word boundaries
|
|
72
|
+
regex = new RegExp(`${escapeRegex(doNot.pattern)}`, 'gi');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Invalid regex, skip
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
let match;
|
|
80
|
+
while ((match = regex.exec(text)) !== null) {
|
|
81
|
+
// Generate a rule ID from the pattern
|
|
82
|
+
const ruleId = doNot.isRegex
|
|
83
|
+
? `doNot.pattern-${doNots.indexOf(doNot)}`
|
|
84
|
+
: `doNot.${slugify(doNot.pattern)}`;
|
|
85
|
+
violations.push(createViolation(ruleId, doNot.severity, doNot.reason, match[0], { start: match.index, end: match.index + match[0].length }, doNot.suggestion));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return violations;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Escape special regex characters
|
|
92
|
+
*/
|
|
93
|
+
function escapeRegex(str) {
|
|
94
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Convert string to slug for rule IDs
|
|
98
|
+
*/
|
|
99
|
+
function slugify(str) {
|
|
100
|
+
return str
|
|
101
|
+
.toLowerCase()
|
|
102
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
103
|
+
.replace(/^-|-$/g, '')
|
|
104
|
+
.slice(0, 30);
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=voice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voice.js","sourceRoot":"","sources":["../../../src/validator/rules/voice.ts"],"names":[],"mappings":"AAEA,IAAI,WAAW,GAAG,CAAC,CAAC;AAEpB,SAAS,eAAe,CACtB,IAAY,EACZ,QAAsC,EACtC,OAAe,EACf,IAAa,EACb,QAAyC,EACzC,UAAmB;IAEnB,OAAO;QACL,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE;QACxB,IAAI;QACJ,QAAQ;QACR,OAAO;QACP,IAAI;QACJ,QAAQ;QACR,UAAU;KACX,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,KAAY;IACxD,MAAM,UAAU,GAAgB,EAAE,CAAC;IAEnC,wBAAwB;IACxB,UAAU,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;IAE1E,+BAA+B;IAC/B,UAAU,CAAC,IAAI,CAAC,GAAG,oBAAoB,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;IAEvE,uBAAuB;IACvB,UAAU,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAE1D,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,SAAS,mBAAmB,CAAC,IAAY,EAAE,SAAmB;IAC5D,MAAM,UAAU,GAAgB,EAAE,CAAC;IACnC,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAErC,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACrC,6BAA6B;QAC7B,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,MAAM,WAAW,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAClE,IAAI,KAAK,CAAC;QAEV,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAC3C,UAAU,CAAC,IAAI,CACb,eAAe,CACb,sBAAsB,EACtB,OAAO,EACP,sBAAsB,IAAI,GAAG,EAC7B,KAAK,CAAC,CAAC,CAAC,EACR,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAC1D,sBAAsB,IAAI,GAAG,CAC9B,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,SAAS,oBAAoB,CAC3B,IAAY,EACZ,KAAmC;IAEnC,MAAM,UAAU,GAAgB,EAAE,CAAC;IAEnC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,MAAM,WAAW,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAC9D,IAAI,KAAK,CAAC;YAEV,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC3C,UAAU,CAAC,IAAI,CACb,eAAe,CACb,sBAAsB,EACtB,SAAS,EACT,QAAQ,IAAI,CAAC,SAAS,iBAAiB,KAAK,GAAG,EAC/C,KAAK,CAAC,CAAC,CAAC,EACR,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAC1D,IAAI,CAAC,SAAS,CACf,CACF,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,IAAY,EAAE,MAAsB;IAC9D,MAAM,UAAU,GAAgB,EAAE,CAAC;IAEnC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAa,CAAC;QAElB,IAAI,CAAC;YACH,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBAClB,KAAK,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAC1C,CAAC;iBAAM,CAAC;gBACN,0CAA0C;gBAC1C,KAAK,GAAG,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;YACtB,SAAS;QACX,CAAC;QAED,IAAI,KAAK,CAAC;QACV,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAC3C,sCAAsC;YACtC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO;gBAC1B,CAAC,CAAC,iBAAiB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;gBAC1C,CAAC,CAAC,SAAS,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YAEtC,UAAU,CAAC,IAAI,CACb,eAAe,CACb,MAAM,EACN,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,MAAM,EACZ,KAAK,CAAC,CAAC,CAAC,EACR,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAC1D,KAAK,CAAC,UAAU,CACjB,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,SAAS,OAAO,CAAC,GAAW;IAC1B,OAAO,GAAG;SACP,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClB,CAAC"}
|