@usequeek/theme-check 0.4.0 → 0.4.1
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/dist/rules/static.js +14 -0
- package/dist/utils/template-copy.js +18 -4
- package/package.json +1 -1
package/dist/rules/static.js
CHANGED
|
@@ -1135,6 +1135,20 @@ export const templateCopyRule = {
|
|
|
1135
1135
|
const findings = [];
|
|
1136
1136
|
for (const store of context.demos) {
|
|
1137
1137
|
const name = store.data?.profile?.name;
|
|
1138
|
+
// The header announcement is copied onto real stores like section copy,
|
|
1139
|
+
// whether the bar is enabled or not — so it reads by the same rules.
|
|
1140
|
+
const announcement = store.data?.config?.header?.announcement?.text;
|
|
1141
|
+
if (typeof announcement === 'string' && announcement !== '') {
|
|
1142
|
+
const why = copyViolations(announcement, typeof name === 'string' ? name : null);
|
|
1143
|
+
if (why.length > 0) {
|
|
1144
|
+
findings.push(finding(context, 'theme/template-copy', 'reject', {
|
|
1145
|
+
where: `${context.env.root}${store.file} → config.header.announcement.text`,
|
|
1146
|
+
found: `text: ${why.join('; ')} — "${announcement.length > 80 ? `${announcement.slice(0, 77)}…` : announcement}"`,
|
|
1147
|
+
fix: 'The setup wizard publishes this copy onto real stores unchanged. Write it for any store in the business: the store\'s name becomes a role ("our kitchen", "the studio"), a place becomes generic ("across the city") or goes, and prices, delivery windows, guarantees and founding dates go — they are the vendor’s to state. Testimonials and reviews are exempt.',
|
|
1148
|
+
docs: `${context.env.docs}#templates`,
|
|
1149
|
+
}));
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1138
1152
|
for (const [pageKey, page] of Object.entries(pagesOf(store))) {
|
|
1139
1153
|
(page?.content ?? []).forEach((section, index) => {
|
|
1140
1154
|
if (typeof section?.type !== 'string')
|
|
@@ -93,10 +93,19 @@ const NAIRA_AMOUNT = [
|
|
|
93
93
|
/(?<![\p{L}\p{N}])N\d{1,3}(,\d{3})+(?![\p{N}])|(?<![\p{L}\p{N}])N\d{3,}(?![\p{L}\p{N}])/u,
|
|
94
94
|
/\d[\d,.]*\s?k?\s?naira\b/i,
|
|
95
95
|
];
|
|
96
|
-
/**
|
|
96
|
+
/**
|
|
97
|
+
* A discount or a coupon code: "Save 20% with code RAINS20", "Up to 25% off".
|
|
98
|
+
* "Half-price" and a bare "sale" are offers too ("the clearance sale is on",
|
|
99
|
+
* "shop the sale", "sale ends Sunday") — but only as their own word, so
|
|
100
|
+
* "wholesale" and "salesperson" are not, and never the trade phrase "for sale
|
|
101
|
+
* by the kilo". "Discount" and "coupon" alone name an offer no store can keep.
|
|
102
|
+
*/
|
|
97
103
|
const OFFER = [
|
|
98
104
|
/\b(?:code|coupon|promo(?: code)?)\s*:?\s*[A-Z][A-Z0-9]{3,}\b/,
|
|
99
105
|
/\b\d{1,3}\s?%\s?(?:off|discount)\b|\bsave\s+(?:up to\s+)?\d{1,3}\s?%|\bup to\s+\d{1,3}\s?%/i,
|
|
106
|
+
/\bhalf[\s-]price\b/i,
|
|
107
|
+
/\b(?<!for )sale\b(?! by the kilo)/i,
|
|
108
|
+
/\b(?:coupon|discount)s?\b/i,
|
|
100
109
|
];
|
|
101
110
|
/**
|
|
102
111
|
* Opening hours: "Open daily", "11am–10pm", "Doors open 11am", "from 6 AM",
|
|
@@ -109,18 +118,23 @@ const HOURS = new RegExp([
|
|
|
109
118
|
String.raw `\b\d{1,2}(?::\d{2})?\s?(?:am|pm)?\s?(?:–|-|to)\s?${TIME}`,
|
|
110
119
|
String.raw `\b(?:opens?|opening|doors|from|till|until|last\s+orders|closes?|closing)\s+(?:at\s+)?${TIME}`,
|
|
111
120
|
String.raw `\b(?:[01]?\d|2[0-3]):[0-5]\d\s?(?:–|-|to)\s?(?:[01]?\d|2[0-3]):[0-5]\d\b`,
|
|
121
|
+
// a weekday at a clock time is the store's schedule: "New drop every Friday, 7pm"
|
|
122
|
+
String.raw `\b(?:mon|tues|wednes|thurs|fri|satur|sun)days?\b,?\s+(?:at\s+)?${TIME}`,
|
|
112
123
|
].join('|'), 'i');
|
|
113
124
|
/**
|
|
114
125
|
* Commitments only the vendor can make. A time window is a promise only next
|
|
115
126
|
* to a service — delivered, ships, answered, fitted, returned — so a recipe's
|
|
116
127
|
* or a process's time ("marinated 24–48 hours", "ready in 3 minutes") is not.
|
|
117
128
|
*/
|
|
118
|
-
const SERVICE = String.raw `(?:deliver\w*|ship(?:s|ped|ping)?|dispatch\w*|arriv\w*|collect\w*|pick(?:ed|s)?[- ]?up|turnaround|answer\w*|repl(?:y|ies|ied)|respond\w*|install\w*|fit(?:ted|ting)?|resiz\w*|repair\w*|replace\w*|exchang\w*|return\w*|refund\w
|
|
119
|
-
const WINDOW = String.raw `(?:(?:within|in|under)\s+(?:about\s+)?(?:\d+|an?|one|two|three|four)[\s-]?(?:min(?:ute)?s?|hours?|hrs?|days?|weeks?)|\d+\s*(?:–|-|to)\s*\d+[\s-]*(?:working\s+|business\s+)?(?:hours?|days?|weeks?)|(?:same|next)[- ](?:day|evening|morning))`;
|
|
129
|
+
const SERVICE = String.raw `(?:deliver\w*|ship(?:s|ped|ping)?|dispatch\w*|arriv\w*|collect\w*|pick(?:ed|s)?[- ]?up|turnaround|answer\w*|repl(?:y|ies|ied)|respond\w*|install\w*|fit(?:ted|ting)?|resiz\w*|repair\w*|replace\w*|exchang\w*|return\w*|refund\w*|measur\w*|tailor\w*|alter(?:s|ed|ing|ations?)?)`;
|
|
130
|
+
const WINDOW = String.raw `(?:(?:within|in|under)\s+(?:about\s+)?(?:\d+|an?|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|fourteen|fifteen|twenty)[\s-]?(?:min(?:ute)?s?|hours?|hrs?|days?|weeks?)|\d+\s*(?:–|-|to)\s*\d+[\s-]*(?:working\s+|business\s+)?(?:hours?|days?|weeks?)|(?:same|next)[- ](?:day|evening|morning))`;
|
|
120
131
|
const PROMISE = [
|
|
121
132
|
new RegExp(String.raw `\b${SERVICE}\b[^.!?\n]{0,40}?${WINDOW}\b|${WINDOW}\b[^.!?\n]{0,40}?\b${SERVICE}\b`, 'i'),
|
|
122
133
|
/\b\d+[\s-](?:minute|min|hour|hr|day|week)s?\s+(?:delivery|dispatch|shipping|turnaround|returns?|exchanges?|refunds?|adjustments?|service)\b/i,
|
|
123
|
-
/\
|
|
134
|
+
/\b(?:free|complimentary)\s+(?:\w+\s+)?(?:delivery|shipping|returns?|pick[- ]?up|collection|alterations?|installation|install|resizing|exchanges?|samples?|styling|gifts?|consultations?|fittings?|refills?)\b/i,
|
|
135
|
+
// a service promised without end, or a reply promised fast
|
|
136
|
+
/\b(?:returns?|exchanges?|refunds?|delivery|shipping)\b[^.!?\n|]{0,20}\balways\b/i,
|
|
137
|
+
/\b(?:answer\w*|repl(?:y|ies|ied)|respond\w*)\s+(?:fast|quickly|promptly|right away)\b|\b(?:fast|quick|prompt)\s+(?:repl(?:y|ies)|answers?|responses?)\b/i,
|
|
124
138
|
/\b(?:guarantee[ds]?|money[- ]back|warrant(?:y|ies)|no questions asked)\b/i,
|
|
125
139
|
];
|
|
126
140
|
/** The store's history, not the business's: "since 2014", "started in 2019", "six years, two stores". */
|