template compliance answers never reach a store: init writes unanswered (dataSafety, appPrivacy, reviewNotes, ageRating, accounts, reviewAccess), older configs are recognised by the template values; check / validate / scan block, play data-safety (also --dry), console plan / fill, apple review-details and appinfo refuse. onboard --declarations replaces them on a live app: name and copyright from the stores (a copyright with a private term is left out), the rows the code proves (scan proposal, sign-up methods from auth code and login SDKs), the rest as one owner question list. play data-safety --dry prints the answers that change (old -> new, account and deletion first) against the last uploaded CSV; pitfall 60; tests
This commit is contained in:
parent
34c2fa22e5
commit
7219ded3e8
27 changed files with 659 additions and 19 deletions
|
|
@ -132,3 +132,72 @@ function depthAt(s: string, at: number): number {
|
|||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
/** End of the value that starts at `i` in `s`: the comma or closing bracket at depth 0. */
|
||||
function valueEnd(s: string, i: number): number {
|
||||
let d = 0;
|
||||
for (; i < s.length; i++) {
|
||||
const ch = s[i]!;
|
||||
if (ch === '/' && s[i + 1] === '/') { const n = s.indexOf('\n', i); if (n < 0) return s.length; i = n; continue; }
|
||||
if (ch === '/' && s[i + 1] === '*') { const n = s.indexOf('*/', i + 2); if (n < 0) return s.length; i = n + 1; continue; }
|
||||
if (ch === '"' || ch === '\'' || ch === '`') { for (i++; i < s.length && s[i] !== ch; i++) if (s[i] === '\\') i++; continue; }
|
||||
if (ch === '{' || ch === '[' || ch === '(') d++;
|
||||
else if (ch === '}' || ch === ']' || ch === ')') { if (d === 0) return i; d--; }
|
||||
else if (ch === ',' && d === 0) return i;
|
||||
}
|
||||
return s.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* The config text without the property at `path` (['dataSafety'], ['apple', 'info', 'reviewNotes']): the key, its
|
||||
* value (any expression), its trailing comma and the comment lines right above it. Unchanged when the path is not
|
||||
* there. .json is parsed and written back.
|
||||
*/
|
||||
export function removeKeyPath(text: string, file: string, path: string[]): string {
|
||||
if (file.endsWith('.json')) {
|
||||
const j = JSON.parse(text) as Record<string, unknown>;
|
||||
let o: Record<string, unknown> | undefined = j;
|
||||
for (const k of path.slice(0, -1)) o = o?.[k] as Record<string, unknown> | undefined;
|
||||
if (!o || typeof o !== 'object' || !(path.at(-1)! in o)) return text;
|
||||
delete o[path.at(-1)!];
|
||||
return JSON.stringify(j, null, 2) + '\n';
|
||||
}
|
||||
let from = 0, to = text.length;
|
||||
for (let n = 0; n < path.length; n++) {
|
||||
const region = text.slice(from, to);
|
||||
const re = new RegExp(`(^|[{,\\s])${path[n]}\\s*:\\s*`, 'gm');
|
||||
let hit: { at: number; val: number } | null = null, bestInd = Infinity;
|
||||
for (let m = re.exec(region); m; m = re.exec(region)) {
|
||||
const at = m.index + m[1]!.length;
|
||||
const lineStart = region.lastIndexOf('\n', at - 1) + 1;
|
||||
if (region.slice(lineStart, at).includes('//')) continue; // in a comment
|
||||
if (n > 0) { if (depthAt(region, at) !== 0) continue; hit = { at: from + at, val: from + m.index + m[0].length }; break; }
|
||||
if (at - lineStart >= bestInd) continue;
|
||||
hit = { at: from + at, val: from + m.index + m[0].length }; bestInd = at - lineStart;
|
||||
}
|
||||
if (!hit) return text;
|
||||
if (n < path.length - 1) {
|
||||
if (text[hit.val] !== '{') return text; // the parent is not an object literal
|
||||
const close = matchBrace(text, hit.val);
|
||||
if (close < 0) return text;
|
||||
from = hit.val + 1; to = close;
|
||||
continue;
|
||||
}
|
||||
let end = valueEnd(text, hit.val);
|
||||
if (text[end] === ',') end++;
|
||||
let start = hit.at;
|
||||
const ls = text.lastIndexOf('\n', start - 1) + 1;
|
||||
if (!text.slice(ls, start).trim()) {
|
||||
start = ls;
|
||||
while (start > 0) {
|
||||
const prev = text.lastIndexOf('\n', start - 2) + 1;
|
||||
if (!/^\s*\/\//.test(text.slice(prev, start - 1))) break;
|
||||
start = prev;
|
||||
}
|
||||
const nl = /^[ \t]*\n/.exec(text.slice(end));
|
||||
if (nl) end += nl[0].length;
|
||||
}
|
||||
return text.slice(0, start) + text.slice(end);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue