跨端: feat(日历): today 不许跨调用残留(常驻 pane 跨零点);扫描口径去注释+去字符串现在就定
pi 2026-09-14 骨架前最后两条。
1. **`today` 必须"显示时求值"**:顶层是「通信 / 日历 / 联系人」三个平级 pane,日历**常驻**,
而 `today` 是唯一随时间变的输入。建页取一次 ⇒ 跨午夜后标记停在昨天,**判据全绿**
(模型没错,是喂进去的值过期了)—— 与 `Calendar.ts` 防的"上次渲染残留"同一件事,
只是残留在**调用侧**。
· 纯逻辑侧判据(新增第 10 条):连续两次不同输入各自独立(不许跨调用残留)、
推进一天后值必须变、**不传就不许有今天**(不许内部兜底取系统时间 —— 那正是"取一次"的写法);
· 调用侧义务(pane 变可见时重算,不许只在 aboutToAppear)进余额:
`calendar-today-recompute`(kind=scope),到期前提写明"骨架写完后立刻"。
2. **`code()` 去注释、不去字符串**:任何只是**提到**被禁写法的字符串会被自己判红。
两条路("报错文案不引被禁模式" vs "只在表达式位置匹配")现在就定,选后者:
先**整段去掉字符串字面量**再匹配 —— "提到"不再误红,"真的用它取日期键"照样红。
副作用写在旁边(正则近似、非解析器;转义/跨行模板处理不干净 ⇒ 是采样不是完备性证明),
并写明:**出现同族误红时不许"再改一次扫描口径"把判据往宽里挪**。
This commit is contained in:
@ -5,6 +5,23 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { readdirSync } from 'node:fs';
|
||||
import { code } from './lib/read.mjs';
|
||||
|
||||
/*
|
||||
* `code()` 去的是**注释**,不是**字符串**(pi 2026-09-14 §2):任何只是"提到"被禁写法的
|
||||
* 字符串(UI 提示、dev 警告、甚至报错文案自己)都会把它自己判红 —— 那不是它要管的东西。
|
||||
*
|
||||
* **口径现在就定下来**(两条路里选"只在表达式位置匹配"):先整段去掉字符串字面量,再匹配。
|
||||
* 于是"提到"不再误红,而**真的用它取日期键**照样红。
|
||||
* 副作用写在旁边,免得下一个人当成漏网:这是**正则近似、不是解析器** ——
|
||||
* 转义引号、跨行模板、嵌套模板都处理不干净,它是采样而非完备性证明。
|
||||
* **出现同族误红时不许"再改一次扫描口径"把判据往宽里挪。**
|
||||
*/
|
||||
function stripStrings(src) {
|
||||
return src
|
||||
.replace(/`(?:[^`\\]|\\.)*`/g, '``')
|
||||
.replace(/'(?:[^'\\\n]|\\.)*'/g, "''")
|
||||
.replace(/"(?:[^"\\\n]|\\.)*"/g, '""');
|
||||
}
|
||||
|
||||
/*
|
||||
★ 日历的纯逻辑(P6 第 1 步的逻辑那一半)—— **不需要设备**,所以它不该进静态欠账。
|
||||
|
||||
@ -152,7 +169,7 @@ test('★ 鸿蒙树里不许用 toISOString 取日期键(今天标错格的直
|
||||
const f = join(dir, e.name);
|
||||
if (e.isDirectory()) { scan(f); continue; }
|
||||
if (!/\.(ets|ts)$/.test(e.name)) continue;
|
||||
code(f).split('\n').forEach((line, i) => {
|
||||
stripStrings(code(f)).split('\n').forEach((line, i) => {
|
||||
if (/toISOString\s*\(\s*\)\s*\.\s*slice\(\s*0\s*,\s*10\s*\)/.test(line)) {
|
||||
hits.push(`${f.slice(ETS.length + 1)}:${i + 1} ${line.trim()}`);
|
||||
}
|
||||
@ -165,3 +182,31 @@ test('★ 鸿蒙树里不许用 toISOString 取日期键(今天标错格的直
|
||||
`"今天"就标到上一格。**正确修法**:页面侧用 model/Calendar.ts 的 isoOfLocal(new Date())。` +
|
||||
`**最常见的错误修法**:把断言里的期望值改成 UTC 日期(那是把判据废掉)。\n ${hits.join('\n ')}`);
|
||||
});
|
||||
|
||||
/*
|
||||
* ★ "今天"必须在显示时求值 —— 日历是**常驻 pane**(pi 2026-09-14 §1)。
|
||||
*
|
||||
* 顶层导航是「通信 / 日历 / 联系人」三个平级 pane,日历长时间挂着;`today` 是**唯一随时间变**
|
||||
* 的输入。建页时取一次存进状态 ⇒ 跨过午夜后标记停在昨天,而**所有判据全绿**
|
||||
* (模型没错,是喂进去的值过期了)。这与 `Calendar.ts` 里防的"上次渲染残留"是同一件事,
|
||||
* 只是残留发生在**调用侧**。
|
||||
*
|
||||
* 纯逻辑侧能钉的是:同一次输入给同一结果、不同输入各自独立、**不许跨调用残留**。
|
||||
* 调用侧义务(pane 变可见时重算)登记在 docs/DEBTS.json 的 `calendar-today-recompute`。
|
||||
*/
|
||||
test('★ today 不许跨调用残留:连续两次不同输入各自独立,且推进一天后值必须变', () => {
|
||||
const mark = g => g.flat().filter(c => c.isToday).map(c => c.iso);
|
||||
assert.deepEqual(mark(C.monthGrid(2026, 9, 1, '2026-09-14')), ['2026-09-14']);
|
||||
assert.deepEqual(mark(C.monthGrid(2026, 9, 1, '2026-09-15')), ['2026-09-15'],
|
||||
'第二次调用必须标 15 日 —— 不许留着上一次的值');
|
||||
|
||||
const t0 = new Date('2026-09-14T23:59:00Z');
|
||||
const t1 = new Date(t0.getTime() + 86400000);
|
||||
const d0 = C.isoAtOffset(t0, 480);
|
||||
const d1 = C.isoAtOffset(t1, 480);
|
||||
assert.notEqual(d0, d1, '跨过零点的两个时刻必须给出不同的今天(否则页面重算也没用)');
|
||||
assert.deepEqual(mark(C.monthGrid(2026, 9, 1, d0)), [d0]);
|
||||
assert.deepEqual(mark(C.monthGrid(2026, 9, 1, d1)), [d1]);
|
||||
assert.equal(mark(C.monthGrid(2026, 9, 1)).length, 0,
|
||||
'不传就不许有今天(不许内部兜底取系统时间 —— 那正是"取一次"的写法)');
|
||||
});
|
||||
|
||||
@ -77,7 +77,7 @@ const SUITE = [
|
||||
['test/appearance-defaults.test.mjs', [], 4],
|
||||
['test/build-stamp.test.mjs', [], 7],
|
||||
['test/packaging.test.mjs', [], 5],
|
||||
['test/harmony-calendar.test.mjs', ['--experimental-strip-types', '--no-warnings'], 9],
|
||||
['test/harmony-calendar.test.mjs', ['--experimental-strip-types', '--no-warnings'], 10],
|
||||
['test/debt-visibility.test.mjs', [], 1],
|
||||
['test/commit-hygiene.test.mjs', ['--experimental-strip-types', '--no-warnings'], 2],
|
||||
// 判据目录自身的卫生:读文本必须走 test/lib/read.mjs 的具名入口
|
||||
|
||||
Reference in New Issue
Block a user