Skip to content

Commit 80684b7

Browse files
committed
chore: enforce code style and format existing sources
1 parent 6a4ff97 commit 80684b7

255 files changed

Lines changed: 58732 additions & 21138 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.editorconfig‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
indent_style = space
6+
indent_size = 2
7+
end_of_line = lf
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true
10+
11+
[*.md]
12+
trim_trailing_whitespace = false
13+
14+
[*.{py,swift}]
15+
indent_size = 4

‎.github/scripts/recent-updates.cjs‎

Lines changed: 66 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,71 @@
1-
const {execFileSync}=require('node:child_process');
2-
const {readFileSync,writeFileSync}=require('node:fs');
1+
const { execFileSync } = require('node:child_process');
2+
const { readFileSync, writeFileSync } = require('node:fs');
33

4-
const feature=/^feat(?:\([^\r\n)]+\))?!?:\s*(.+)$/;
5-
const git=(...args)=>execFileSync('git',args,{encoding:'utf8'}).trim();
6-
const escape=value=>String(value).replace(/[\\`*_[\]<>|]/g,char=>`\\${char}`).replace(/[\r\n]+/g,' ');
4+
const feature = /^feat(?:\([^\r\n)]+\))?!?:\s*(.+)$/;
5+
const git = (...args) => execFileSync('git', args, { encoding: 'utf8' }).trim();
6+
const escape = (value) =>
7+
String(value)
8+
.replace(/[\\`*_[\]<>|]/g, (char) => `\\${char}`)
9+
.replace(/[\r\n]+/g, ' ');
710

8-
function replaceUpdates(readme,rows){
9-
const section=/^## Recent updates\r?\n[\s\S]*?(?=^## |$(?![\s\S]))/m;
10-
if(!section.test(readme))throw Error('README.md must contain a Recent updates section.');
11-
return readme.replace(section,()=>`## Recent updates\n\n| Date | Feature | Contributor |\n| --- | --- | --- |\n${rows.join('\n')}\n\n`);
11+
function replaceUpdates(readme, rows) {
12+
const section = /^## Recent updates\r?\n[\s\S]*?(?=^## |$(?![\s\S]))/m;
13+
if (!section.test(readme)) throw Error('README.md must contain a Recent updates section.');
14+
return readme.replace(
15+
section,
16+
() =>
17+
`## Recent updates\n\n| Date | Feature | Contributor |\n| --- | --- | --- |\n${rows.join('\n')}\n\n`,
18+
);
1219
}
1320

14-
module.exports=async({github,context,core})=>{
15-
const {before,after,created,deleted}=context.payload;
16-
if(deleted)return;
17-
const range=created||!before||/^0+$/.test(before)?[after]:[`${before}..${after}`];
18-
// A multi-commit push can contain feat even when its newest commit is a fix.
19-
const subjects=git('log','--format=%s',...range).split('\n');
20-
if(!subjects.some(subject=>feature.test(subject))){core.info('No feat commits in this push.');return;}
21-
const readme=readFileSync('README.md','utf8');
22-
const previous=new Map([...readme.matchAll(/\[([^\n]+?)\]\(https:\/\/[^\s)]+\/commit\/([a-f0-9]{40})\)/g)].map(match=>[match[2],match[1]]));
23-
const history=git('log','--format=%H%x00%aI%x00%s').split('\n').map(line=>line.split('\0'));
24-
const updates=[];
25-
for(const [sha,date,subject] of history){
26-
const match=subject.match(feature);if(!match)continue;
27-
// Badge/documentation-only commits do not describe an application feature.
28-
const files=git('diff-tree','--root','--no-commit-id','--name-only','-r',sha).split('\n').filter(Boolean);
29-
if(!files.some(file=>!file.endsWith('.md')&&!file.startsWith('doc/')))continue;
30-
updates.push({sha,date,subject:match[1]});
31-
}
32-
updates.sort((a,b)=>Date.parse(b.date)-Date.parse(a.date));
33-
const rows=await Promise.all(updates.slice(0,7).map(async({sha,date,subject})=>{
34-
const {data}=await github.rest.repos.getCommit({...context.repo,ref:sha});
35-
const author=data.author;
36-
const contributor=author?`[@${escape(author.login)}](${author.html_url})`:`[${escape(data.commit.author.name)}](${data.html_url})`;
37-
// Use the calendar date recorded by Git; display no time or timezone.
38-
return `| ${date.slice(0,10)} | [${previous.get(sha)||escape(subject)}](${data.html_url}) | ${contributor} |`;
39-
}));
40-
const updated=replaceUpdates(readme,rows);
41-
if(updated===readme){core.info('Recent updates already current.');return;}
42-
writeFileSync('README.md',updated);
43-
core.setOutput('changed','true');
21+
module.exports = async ({ github, context, core }) => {
22+
const { before, after, created, deleted } = context.payload;
23+
if (deleted) return;
24+
const range = created || !before || /^0+$/.test(before) ? [after] : [`${before}..${after}`];
25+
// A multi-commit push can contain feat even when its newest commit is a fix.
26+
const subjects = git('log', '--format=%s', ...range).split('\n');
27+
if (!subjects.some((subject) => feature.test(subject))) {
28+
core.info('No feat commits in this push.');
29+
return;
30+
}
31+
const readme = readFileSync('README.md', 'utf8');
32+
const previous = new Map(
33+
[...readme.matchAll(/\[([^\n]+?)\]\(https:\/\/[^\s)]+\/commit\/([a-f0-9]{40})\)/g)].map(
34+
(match) => [match[2], match[1]],
35+
),
36+
);
37+
const history = git('log', '--format=%H%x00%aI%x00%s')
38+
.split('\n')
39+
.map((line) => line.split('\0'));
40+
const updates = [];
41+
for (const [sha, date, subject] of history) {
42+
const match = subject.match(feature);
43+
if (!match) continue;
44+
// Badge/documentation-only commits do not describe an application feature.
45+
const files = git('diff-tree', '--root', '--no-commit-id', '--name-only', '-r', sha)
46+
.split('\n')
47+
.filter(Boolean);
48+
if (!files.some((file) => !file.endsWith('.md') && !file.startsWith('doc/'))) continue;
49+
updates.push({ sha, date, subject: match[1] });
50+
}
51+
updates.sort((a, b) => Date.parse(b.date) - Date.parse(a.date));
52+
const rows = await Promise.all(
53+
updates.slice(0, 7).map(async ({ sha, date, subject }) => {
54+
const { data } = await github.rest.repos.getCommit({ ...context.repo, ref: sha });
55+
const author = data.author;
56+
const contributor = author
57+
? `[@${escape(author.login)}](${author.html_url})`
58+
: `[${escape(data.commit.author.name)}](${data.html_url})`;
59+
// Use the calendar date recorded by Git; display no time or timezone.
60+
return `| ${date.slice(0, 10)} | [${previous.get(sha) || escape(subject)}](${data.html_url}) | ${contributor} |`;
61+
}),
62+
);
63+
const updated = replaceUpdates(readme, rows);
64+
if (updated === readme) {
65+
core.info('Recent updates already current.');
66+
return;
67+
}
68+
writeFileSync('README.md', updated);
69+
core.setOutput('changed', 'true');
4470
};
45-
module.exports.replaceUpdates=replaceUpdates;
71+
module.exports.replaceUpdates = replaceUpdates;
Lines changed: 88 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,91 @@
1-
const {test}=require('node:test');
2-
const assert=require('node:assert/strict');
3-
const {execFileSync}=require('node:child_process');
4-
const {mkdtempSync,writeFileSync,readFileSync,rmSync}=require('node:fs');
5-
const {join}=require('node:path');
6-
const {tmpdir}=require('node:os');
7-
const update=require('./recent-updates.cjs');
1+
const { test } = require('node:test');
2+
const assert = require('node:assert/strict');
3+
const { execFileSync } = require('node:child_process');
4+
const { mkdtempSync, writeFileSync, readFileSync, rmSync } = require('node:fs');
5+
const { join } = require('node:path');
6+
const { tmpdir } = require('node:os');
7+
const update = require('./recent-updates.cjs');
88

9-
test('multi-commit pushes update only English README, keep seven dated features and remain idempotent',async()=>{
10-
const root=mkdtempSync(join(tmpdir(),'reader-recent-updates-')),previous=process.cwd();
11-
const git=(...args)=>execFileSync('git',args,{cwd:root,encoding:'utf8',stdio:['ignore','pipe','pipe']}).trim();
12-
try{
13-
git('init');git('config','user.name','Contributor');git('config','user.email','contributor@example.com');
14-
const original='Intro\n\n## Features\n\nKeep features\n\n## Recent updates\n\nOld table\n\n## Quick start\n\nKeep quick start\n';
15-
writeFileSync(join(root,'README.md'),original);writeFileSync(join(root,'translated.md'),'Keep translation');
16-
git('add','.');git('commit','-m','initial');const before=git('rev-parse','HEAD');
17-
for(let n=1;n<=9;n++){
18-
writeFileSync(join(root,'app.js'),`// feature ${n}\n`);git('add','app.js');git('commit','-m',`feat(reader): feature ${n}`);
9+
test('multi-commit pushes update only English README, keep seven dated features and remain idempotent', async () => {
10+
const root = mkdtempSync(join(tmpdir(), 'reader-recent-updates-')),
11+
previous = process.cwd();
12+
const git = (...args) =>
13+
execFileSync('git', args, {
14+
cwd: root,
15+
encoding: 'utf8',
16+
stdio: ['ignore', 'pipe', 'pipe'],
17+
}).trim();
18+
try {
19+
git('init');
20+
git('config', 'user.name', 'Contributor');
21+
git('config', 'user.email', 'contributor@example.com');
22+
const original =
23+
'Intro\n\n## Features\n\nKeep features\n\n## Recent updates\n\nOld table\n\n## Quick start\n\nKeep quick start\n';
24+
writeFileSync(join(root, 'README.md'), original);
25+
writeFileSync(join(root, 'translated.md'), 'Keep translation');
26+
git('add', '.');
27+
git('commit', '-m', 'initial');
28+
const before = git('rev-parse', 'HEAD');
29+
for (let n = 1; n <= 9; n++) {
30+
writeFileSync(join(root, 'app.js'), `// feature ${n}\n`);
31+
git('add', 'app.js');
32+
git('commit', '-m', `feat(reader): feature ${n}`);
33+
}
34+
const newest = git('rev-parse', 'HEAD');
35+
writeFileSync(join(root, 'README.md'), original + '\nBadge\n');
36+
git('add', 'README.md');
37+
git('commit', '-m', 'feat: ci badge');
38+
writeFileSync(join(root, 'app.js'), '// fix\n');
39+
git('add', 'app.js');
40+
git('commit', '-m', 'fix: last commit');
41+
const after = git('rev-parse', 'HEAD');
42+
let calls = 0;
43+
const outputs = [];
44+
const github = {
45+
rest: {
46+
repos: {
47+
getCommit: async ({ ref }) => {
48+
calls++;
49+
return {
50+
data: {
51+
author: { login: 'contributor', html_url: 'https://github.com/contributor' },
52+
html_url: `https://github.com/owner/repo/commit/${ref}`,
53+
},
54+
};
55+
},
56+
},
57+
},
58+
};
59+
const core = { info: () => {}, setOutput: (...args) => outputs.push(args) };
60+
const context = { repo: { owner: 'owner', repo: 'repo' }, payload: { before, after } };
61+
process.chdir(root);
62+
await update({ github, context, core });
63+
const result = readFileSync('README.md', 'utf8');
64+
assert.equal(calls, 7);
65+
assert.equal((result.match(/^\| \d{4}-\d{2}-\d{2} \|/gm) || []).length, 7);
66+
assert.ok(result.includes(`[feature 9](https://github.com/owner/repo/commit/${newest})`));
67+
assert.ok(!result.includes('ci badge'));
68+
assert.ok(!result.includes('feature 2]'));
69+
assert.ok(result.includes('Keep features'));
70+
assert.ok(result.includes('Keep quick start\n\nBadge'));
71+
assert.ok(result.includes('[@contributor](https://github.com/contributor)'));
72+
assert.equal(readFileSync('translated.md', 'utf8'), 'Keep translation');
73+
assert.deepEqual(outputs, [['changed', 'true']]);
74+
outputs.length = 0;
75+
await update({ github, context, core });
76+
assert.deepEqual(outputs, []);
77+
// A fix-only push performs no API calls or file writes.
78+
const callCount = calls;
79+
context.payload.before = git('rev-parse', 'HEAD^');
80+
await update({ github, context, core });
81+
assert.equal(calls, callCount);
82+
assert.equal(readFileSync('README.md', 'utf8'), result);
83+
// New branches have an all-zero before SHA.
84+
context.payload = { before: '0'.repeat(40), after, created: true };
85+
await update({ github, context, core });
86+
assert.equal(readFileSync('README.md', 'utf8'), result);
87+
} finally {
88+
process.chdir(previous);
89+
rmSync(root, { recursive: true, force: true });
1990
}
20-
const newest=git('rev-parse','HEAD');
21-
writeFileSync(join(root,'README.md'),original+'\nBadge\n');git('add','README.md');git('commit','-m','feat: ci badge');
22-
writeFileSync(join(root,'app.js'),'// fix\n');git('add','app.js');git('commit','-m','fix: last commit');const after=git('rev-parse','HEAD');
23-
let calls=0;const outputs=[];
24-
const github={rest:{repos:{getCommit:async({ref})=>{calls++;return {data:{author:{login:'contributor',html_url:'https://github.com/contributor'},html_url:`https://github.com/owner/repo/commit/${ref}`}};}}}};
25-
const core={info:()=>{},setOutput:(...args)=>outputs.push(args)};
26-
const context={repo:{owner:'owner',repo:'repo'},payload:{before,after}};
27-
process.chdir(root);
28-
await update({github,context,core});
29-
const result=readFileSync('README.md','utf8');
30-
assert.equal(calls,7);assert.equal((result.match(/^\| \d{4}-\d{2}-\d{2} \|/gm)||[]).length,7);
31-
assert.ok(result.includes(`[feature 9](https://github.com/owner/repo/commit/${newest})`));
32-
assert.ok(!result.includes('ci badge'));assert.ok(!result.includes('feature 2]'));
33-
assert.ok(result.includes('Keep features'));assert.ok(result.includes('Keep quick start\n\nBadge'));
34-
assert.ok(result.includes('[@contributor](https://github.com/contributor)'));
35-
assert.equal(readFileSync('translated.md','utf8'),'Keep translation');
36-
assert.deepEqual(outputs,[['changed','true']]);
37-
outputs.length=0;await update({github,context,core});assert.deepEqual(outputs,[]);
38-
// A fix-only push performs no API calls or file writes.
39-
const callCount=calls;
40-
context.payload.before=git('rev-parse','HEAD^');await update({github,context,core});
41-
assert.equal(calls,callCount);assert.equal(readFileSync('README.md','utf8'),result);
42-
// New branches have an all-zero before SHA.
43-
context.payload={before:'0'.repeat(40),after,created:true};
44-
await update({github,context,core});assert.equal(readFileSync('README.md','utf8'),result);
45-
}finally{process.chdir(previous);rmSync(root,{recursive:true,force:true});}
4691
});

‎.github/workflows/code-style.yml‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Code style
2+
on:
3+
push:
4+
pull_request:
5+
workflow_dispatch:
6+
permissions:
7+
contents: read
8+
jobs:
9+
style:
10+
runs-on: xcode-27
11+
env:
12+
ELECTRON_SKIP_BINARY_DOWNLOAD: '1'
13+
steps:
14+
- uses: actions/checkout@v5
15+
- uses: actions/setup-node@v5
16+
with:
17+
node-version: '22'
18+
cache: npm
19+
- uses: astral-sh/setup-uv@v7
20+
- run: npm ci --no-audit --no-fund
21+
- run: npm run style:check

‎.husky/pre-commit‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
npm exec --no -- lint-staged

‎.prettierignore‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
node_modules/
2+
dist/
3+
release/
4+
.cache/
5+
.codegraph/
6+
.cursor/
7+
public/symbols/
8+
src/build-info.mjs
9+
package-lock.json
10+
doc/**/*-baseline.json
11+
doc/**/*-optimized.json
12+
doc/**/*-result.json
13+
doc/**/*-results.json

‎.prettierrc.json‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"singleQuote": true,
3+
"printWidth": 100,
4+
"trailingComma": "all",
5+
"endOfLine": "lf"
6+
}

‎.swift-format‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"version": 1,
3+
"lineLength": 100,
4+
"indentation": { "spaces": 4 }
5+
}

0 commit comments

Comments
 (0)