feat: add phone frequency controls and modularize codebase
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const root = process.cwd();
|
||||
const contractPath = path.join(root, 'docs/contracts/admin-sms-records-r11.json');
|
||||
const contract = JSON.parse(fs.readFileSync(contractPath, 'utf8'));
|
||||
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
|
||||
const failures = [];
|
||||
|
||||
function requirePattern(source, pattern, message) {
|
||||
if (!pattern.test(source)) failures.push(message);
|
||||
}
|
||||
|
||||
const entry = read(contract.stableEntry);
|
||||
const entryLines = entry.split(/\r?\n/).length;
|
||||
requirePattern(
|
||||
entry,
|
||||
new RegExp(`export\\s+function\\s+${contract.stableExport}\\s*\\(`),
|
||||
`stable export ${contract.stableExport} is missing`,
|
||||
);
|
||||
if (entryLines > contract.maxStableEntryLines) {
|
||||
failures.push(`stable entry has ${entryLines} lines; maximum is ${contract.maxStableEntryLines}`);
|
||||
}
|
||||
if (!entry.includes(`import '${contract.styleImport}';`)) {
|
||||
failures.push(`stable entry does not import ${contract.styleImport}`);
|
||||
}
|
||||
|
||||
for (const [file, exports] of Object.entries(contract.modules)) {
|
||||
const source = read(file);
|
||||
for (const exportedName of exports) {
|
||||
requirePattern(
|
||||
source,
|
||||
new RegExp(`export\\s+(?:function|const|class)\\s+${exportedName}\\b`),
|
||||
`${file} is missing export ${exportedName}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [file, methods] of Object.entries(contract.apiCalls)) {
|
||||
const source = read(file);
|
||||
for (const method of methods) {
|
||||
requirePattern(source, new RegExp(`adminApi\\.${method}\\b`), `${file} is missing adminApi.${method}`);
|
||||
}
|
||||
}
|
||||
|
||||
const style = read(contract.styleFile);
|
||||
const globalStyle = read('src/styles/global.css');
|
||||
const componentStyle = read('src/styles/components.css');
|
||||
for (const selector of contract.pageStyleSelectors) {
|
||||
if (!style.includes(selector)) failures.push(`${contract.styleFile} is missing ${selector}`);
|
||||
if (globalStyle.includes(selector)) failures.push(`src/styles/global.css still contains page selector ${selector}`);
|
||||
}
|
||||
for (const selector of contract.sharedSelectorsKeptGlobal) {
|
||||
if (!globalStyle.includes(selector)) failures.push(`shared selector ${selector} was removed from global.css`);
|
||||
}
|
||||
for (const selector of contract.sharedSelectorsKeptInComponents) {
|
||||
if (!componentStyle.includes(selector)) failures.push(`shared selector ${selector} was removed from components.css`);
|
||||
if (globalStyle.includes(selector)) failures.push(`shared selector ${selector} still exists in global.css`);
|
||||
}
|
||||
for (const label of contract.interactionLabels) {
|
||||
const found = [entry, ...Object.keys(contract.modules).map(read)].some((source) => source.includes(label));
|
||||
if (!found) failures.push(`interaction label ${label} is missing`);
|
||||
}
|
||||
|
||||
requirePattern(
|
||||
style,
|
||||
/@media\s*\(max-width:\s*900px\)[\s\S]*?\.admin-sms-record-card__meta[\s\S]*?\.admin-sms-segment-card dl/,
|
||||
'900px record/detail responsive rule is missing',
|
||||
);
|
||||
requirePattern(
|
||||
style,
|
||||
/@media\s*\(max-width:\s*780px\)[\s\S]*?\.admin-sms-record-filter[\s\S]*?\.admin-sms-route-list dl/,
|
||||
'780px record/detail responsive rule is missing',
|
||||
);
|
||||
|
||||
if (failures.length > 0) {
|
||||
console.error(`R11 admin SMS records verification failed:\n- ${failures.join('\n- ')}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(
|
||||
`R11 admin SMS records verified: ${entryLines} stable-entry lines, `
|
||||
+ `${Object.keys(contract.modules).length} focused modules, real API boundaries and page-scoped CSS preserved.`,
|
||||
);
|
||||
Reference in New Issue
Block a user