Chore: say hello to bun

This commit is contained in:
SukkaW 2023-11-15 12:13:52 +08:00
parent 0e8bfc0def
commit 37257958c2
10 changed files with 113 additions and 1895 deletions

View File

@ -15,19 +15,11 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
persist-credentials: false persist-credentials: false
- uses: pnpm/action-setup@v2 - uses: oven-sh/setup-bun@v1
name: Install pnpm
id: pnpm-install
with: with:
version: latest bun-version: latest
run_install: false - run: bun install
- name: Use Node.js - run: bun run build
uses: actions/setup-node@v3
with:
node-version-file: '.node-version'
cache: 'pnpm'
- run: pnpm i
- run: pnpm run build
- name: Deploy - name: Deploy
uses: peaceiris/actions-gh-pages@v3 uses: peaceiris/actions-gh-pages@v3
with: with:

View File

@ -35,6 +35,8 @@ const buildInternalCDNDomains = task(__filename, async () => {
*/ */
const processLocalDomainSet = async (domainSetPath) => { const processLocalDomainSet = async (domainSetPath) => {
for await (const line of readFileByLine(domainSetPath)) { for await (const line of readFileByLine(domainSetPath)) {
// console.log({ line });
const parsed = tldts.parse(line, { allowPrivateDomains: true, detectIp: false }); const parsed = tldts.parse(line, { allowPrivateDomains: true, detectIp: false });
if (parsed.isIp) continue; if (parsed.isIp) continue;
if (parsed.isIcann || parsed.isPrivate) { if (parsed.isIcann || parsed.isPrivate) {

View File

@ -1,56 +1,42 @@
// @ts-check import { downloadPreviousBuild, downloadPublicSuffixList } from './download-previous-build';
import { buildCommon } from './build-common';
import { buildAntiBogusDomain } from './build-anti-bogus-domain';
import { buildAppleCdn } from './build-apple-cdn';
import { buildCdnConf } from './build-cdn-conf';
import { buildPhishingDomainSet } from './build-phishing-domainset';
import { buildRejectDomainSet } from './build-reject-domainset';
import { buildTelegramCIDR } from './build-telegram-cidr';
import { buildChnCidr } from './build-chn-cidr';
import { buildSpeedtestDomainSet } from './build-speedtest-domainset';
import { buildInternalCDNDomains } from './build-internal-cdn-rules';
import { buildInternalChnDomains } from './build-internal-chn-domains';
import { buildDomesticRuleset } from './build-domestic-ruleset';
import { buildStreamService } from './build-stream-service';
import { buildRedirectModule } from './build-redirect-module';
import { validate } from './validate-domainset';
const { downloadPreviousBuild, downloadPublicSuffixList } = require('./download-previous-build'); import { buildPublicHtml } from './build-public';
const { buildCommon } = require('./build-common');
const { buildAntiBogusDomain } = require('./build-anti-bogus-domain');
const { buildAppleCdn } = require('./build-apple-cdn');
const { buildCdnConf } = require('./build-cdn-conf');
const { buildPhishingDomainSet } = require('./build-phishing-domainset');
const { buildRejectDomainSet } = require('./build-reject-domainset');
const { buildTelegramCIDR } = require('./build-telegram-cidr');
const { buildChnCidr } = require('./build-chn-cidr');
const { buildSpeedtestDomainSet } = require('./build-speedtest-domainset');
const { buildInternalCDNDomains } = require('./build-internal-cdn-rules');
const { buildInternalChnDomains } = require('./build-internal-chn-domains');
const { buildDomesticRuleset } = require('./build-domestic-ruleset');
const { buildStreamService } = require('./build-stream-service');
const { buildRedirectModule } = require('./build-redirect-module');
const { validate } = require('./validate-domainset');
const { buildPublicHtml } = require('./build-public'); import { Worker } from 'jest-worker';
const { Worker } = require('jest-worker'); type WithWorker<T> = import('jest-worker').Worker & { __sukka_worker_name: string } & T
/** const requireWorker = <T>(path: string): WithWorker<T> => {
* @template T const _worker = new Worker(
* @typedef {import('jest-worker').Worker & { __sukka_worker_name: string } & T} WithWorker
*/
/**
* @template T
* @param {string} path
* @returns {WithWorker<T>}
*/
const requireWorker = (path) => {
const _worker = /** @type {WithWorker<T>} */ (new Worker(
require.resolve(path), require.resolve(path),
{ {
numWorkers: 1, numWorkers: 1,
maxRetries: 0, maxRetries: 0,
enableWorkerThreads: true enableWorkerThreads: true
} }
)); ) as WithWorker<T>;
_worker.getStderr().pipe(process.stderr); _worker.getStderr().pipe(process.stderr);
_worker.getStdout().pipe(process.stdout); _worker.getStdout().pipe(process.stdout);
_worker.__sukka_worker_name = path; _worker.__sukka_worker_name = path;
return _worker; return _worker;
}; };
/** const endWorker = async <T>(worker: WithWorker<T>) => {
* @template T
* @param {WithWorker<T>} worker
*/
const endWorker = async (worker) => {
const { forceExited } = await worker.end(); const { forceExited } = await worker.end();
if (forceExited && worker.__sukka_worker_name) { if (forceExited && worker.__sukka_worker_name) {
console.log(worker.__sukka_worker_name, 'forceExited'); console.log(worker.__sukka_worker_name, 'forceExited');
@ -58,57 +44,43 @@ const endWorker = async (worker) => {
}; };
(async () => { (async () => {
const buildInternalReverseChnCIDRWorker = /** @type {WithWorker<import('./build-internal-reverse-chn-cidr')>} */ (requireWorker('./build-internal-reverse-chn-cidr')); const buildInternalReverseChnCIDRWorker: WithWorker<typeof import('./build-internal-reverse-chn-cidr')> = requireWorker('./build-internal-reverse-chn-cidr');
const { buildInternalReverseChnCIDR } = buildInternalReverseChnCIDRWorker; const { buildInternalReverseChnCIDR } = buildInternalReverseChnCIDRWorker;
// download-previous-build
const downloadPreviousBuildPromise = downloadPreviousBuild(); const downloadPreviousBuildPromise = downloadPreviousBuild();
const downloadPublicSuffixListPromise = downloadPublicSuffixList(); const downloadPublicSuffixListPromise = downloadPublicSuffixList();
// build:common
const buildCommonPromise = downloadPreviousBuildPromise.then(() => buildCommon()); const buildCommonPromise = downloadPreviousBuildPromise.then(() => buildCommon());
// build:anti-bogus-domain
const buildAntiBogusDomainPromise = downloadPreviousBuildPromise.then(() => buildAntiBogusDomain()); const buildAntiBogusDomainPromise = downloadPreviousBuildPromise.then(() => buildAntiBogusDomain());
// build:apple-cdn
const buildAppleCdnPromise = downloadPreviousBuildPromise.then(() => buildAppleCdn()); const buildAppleCdnPromise = downloadPreviousBuildPromise.then(() => buildAppleCdn());
// build:cdn-conf
const buildCdnConfPromise = Promise.all([ const buildCdnConfPromise = Promise.all([
downloadPreviousBuildPromise, downloadPreviousBuildPromise,
downloadPublicSuffixListPromise downloadPublicSuffixListPromise
]).then(() => buildCdnConf()); ]).then(() => buildCdnConf());
// build:phishing-domainset
const buildPhilishingDomainsetPromise = Promise.all([ const buildPhilishingDomainsetPromise = Promise.all([
downloadPreviousBuildPromise, downloadPreviousBuildPromise,
downloadPublicSuffixListPromise downloadPublicSuffixListPromise
]).then(() => buildPhishingDomainSet()); ]).then(() => buildPhishingDomainSet());
// build:reject-domainset
const buildRejectDomainSetPromise = Promise.all([ const buildRejectDomainSetPromise = Promise.all([
downloadPreviousBuildPromise, downloadPreviousBuildPromise,
downloadPublicSuffixListPromise, downloadPublicSuffixListPromise,
buildPhilishingDomainsetPromise buildPhilishingDomainsetPromise
]).then(() => buildRejectDomainSet()); ]).then(() => buildRejectDomainSet());
// build:telegram-cidr
const buildTelegramCIDRPromise = downloadPreviousBuildPromise.then(() => buildTelegramCIDR()); const buildTelegramCIDRPromise = downloadPreviousBuildPromise.then(() => buildTelegramCIDR());
// build:chn-cidr
const buildChnCidrPromise = downloadPreviousBuildPromise.then(() => buildChnCidr()); const buildChnCidrPromise = downloadPreviousBuildPromise.then(() => buildChnCidr());
// build:speedtest-domainset
const buildSpeedtestDomainSetPromise = downloadPreviousBuildPromise.then(() => buildSpeedtestDomainSet()); const buildSpeedtestDomainSetPromise = downloadPreviousBuildPromise.then(() => buildSpeedtestDomainSet());
// build:internal-cdn-rules
const buildInternalCDNDomainsPromise = Promise.all([ const buildInternalCDNDomainsPromise = Promise.all([
downloadPublicSuffixListPromise, downloadPublicSuffixListPromise,
buildCommonPromise, buildCommonPromise,
buildCdnConfPromise buildCdnConfPromise
]).then(() => buildInternalCDNDomains()); ]).then(() => buildInternalCDNDomains());
// build:internal-reverse-chn-cidr
const buildInternalReverseChnCIDRPromise = buildInternalReverseChnCIDR(); const buildInternalReverseChnCIDRPromise = buildInternalReverseChnCIDR();
// build:internal-chn-domains
const buildInternalChnDomainsPromise = buildInternalChnDomains(); const buildInternalChnDomainsPromise = buildInternalChnDomains();
// build:domestic-ruleset
const buildDomesticRulesetPromise = downloadPreviousBuildPromise.then(() => buildDomesticRuleset()); const buildDomesticRulesetPromise = downloadPreviousBuildPromise.then(() => buildDomesticRuleset());
const buildRedirectModulePromise = downloadPreviousBuildPromise.then(() => buildRedirectModule()); const buildRedirectModulePromise = downloadPreviousBuildPromise.then(() => buildRedirectModule());
const buildStreamServicePromise = downloadPreviousBuildPromise.then(() => buildStreamService()); const buildStreamServicePromise = downloadPreviousBuildPromise.then(() => buildStreamService());
const stats = await Promise.all([ const stats: Array<{ start: number, end: number, taskName: string }> = await Promise.all([
downloadPreviousBuildPromise, downloadPreviousBuildPromise,
downloadPublicSuffixListPromise, downloadPublicSuffixListPromise,
buildCommonPromise, buildCommonPromise,
@ -137,20 +109,16 @@ const endWorker = async (worker) => {
printStats(stats); printStats(stats);
})(); })();
/** function printStats(stats: Array<{ start: number, end: number, taskName: string }>): void {
* @param {Array<{ start: number, end: number, taskName: string }>} stats
*/
function printStats(stats) {
// sort stats by start time
stats.sort((a, b) => a.start - b.start); stats.sort((a, b) => a.start - b.start);
const longestTaskName = Math.max(...stats.map(i => i.taskName.length)); const longestTaskName: number = Math.max(...stats.map(i => i.taskName.length));
const realStart = Math.min(...stats.map(i => i.start)); const realStart: number = Math.min(...stats.map(i => i.start));
const realEnd = Math.max(...stats.map(i => i.end)); const realEnd: number = Math.max(...stats.map(i => i.end));
const totalMs = realEnd - realStart; const totalMs: number = realEnd - realStart;
const statsStep = (totalMs / 160) | 0; const statsStep: number = (totalMs / 160) | 0;
stats.forEach(stat => { stats.forEach(stat => {
console.log( console.log(

View File

@ -44,35 +44,21 @@ async function compareAndWriteFile(linesA, filePath) {
} }
if (!isEqual) { if (!isEqual) {
const stream = fs.createWriteStream(filePath, { encoding: 'utf-8' }); const file = Bun.file(filePath);
const writer = file.writer();
for (let i = 0, len = linesA.length; i < len; i++) { for (let i = 0, len = linesA.length; i < len; i++) {
const p = writeToStream(stream, `${linesA[i]}\n`); writer.write(`${linesA[i]}\n`);
if (p) {
// eslint-disable-next-line no-await-in-loop -- backpressure, besides we only wait for drain
await p;
}
} }
stream.end();
} else { await writer.end();
console.log(`Same Content, bail out writing: ${filePath}`); return;
} }
console.log(`Same Content, bail out writing: ${filePath}`);
} }
module.exports.compareAndWriteFile = compareAndWriteFile; module.exports.compareAndWriteFile = compareAndWriteFile;
/**
* @param {import('fs').WriteStream} stream
* @param {string} data
*/
function writeToStream(stream, data) {
if (!stream.write(data)) {
return /** @type {Promise<void>} */(new Promise((resolve) => {
stream.once('drain', resolve);
}));
}
return null;
}
/** /**
* @param {string} title * @param {string} title
* @param {string[]} description * @param {string[]} description

View File

@ -1,33 +1,60 @@
// @ts-check // @ts-check
const fs = require('fs');
const { fetchWithRetry } = require('./fetch-retry'); const { fetchWithRetry } = require('./fetch-retry');
const readline = require('readline');
const { Readable } = require('stream');
const decoder = new TextDecoder('utf-8');
/** /**
* @param {string} path * @param {string} path
*/ */
module.exports.readFileByLine = (path) => { module.exports.readFileByLine = async function *(path) {
return readline.createInterface({ let buf = '';
input: fs.createReadStream(path, { encoding: 'utf-8' }),
crlfDelay: Infinity for await (const chunk of Bun.file(path).stream()) {
}); const chunkStr = decoder.decode(chunk).replaceAll('\r\n', '\n');
for (let i = 0, len = chunkStr.length; i < len; i++) {
const char = chunkStr[i];
if (char === '\n') {
yield buf;
buf = '';
} else {
buf += char;
}
}
}
if (buf) {
yield buf;
}
}; };
/** /**
* @param {import('undici').Response} resp * @param {import('undici').Response} resp
*/ */
const createReadlineInterfaceFromResponse = (resp) => { const createReadlineInterfaceFromResponse = async function *(resp) {
if (!resp.body) { if (!resp.body) {
throw new Error('Failed to fetch remote text'); throw new Error('Failed to fetch remote text');
} }
if (resp.bodyUsed) { if (resp.bodyUsed) {
throw new Error('Body has already been consumed.'); throw new Error('Body has already been consumed.');
} }
return readline.createInterface({
input: Readable.fromWeb(resp.body), let buf = '';
crlfDelay: Infinity
}); for await (const chunk of resp.body) {
const chunkStr = decoder.decode(chunk).replaceAll('\r\n', '\n');
for (let i = 0, len = chunkStr.length; i < len; i++) {
const char = chunkStr[i];
if (char === '\n') {
yield buf;
buf = '';
} else {
buf += char;
}
}
}
if (buf) {
yield buf;
}
}; };
module.exports.createReadlineInterfaceFromResponse = createReadlineInterfaceFromResponse; module.exports.createReadlineInterfaceFromResponse = createReadlineInterfaceFromResponse;

BIN
bun.lockb Executable file

Binary file not shown.

View File

@ -2,7 +2,12 @@
module.exports = require('eslint-config-sukka').sukka({ module.exports = require('eslint-config-sukka').sukka({
js: { js: {
disableNoConsoleInCLI: ['Build/**'] disableNoConsoleInCLI: ['Build/**'],
env: {
customGlobals: {
'Bun': 'readonly'
}
}
}, },
node: true node: true
}, { }, {

View File

@ -8,7 +8,7 @@
"url": "git+https://github.com/SukkaW/Surge.git" "url": "git+https://github.com/SukkaW/Surge.git"
}, },
"scripts": { "scripts": {
"build": "node ./Build/index.js", "build": "bun ./Build/index.ts",
"lint": "eslint --format=sukka ." "lint": "eslint --format=sukka ."
}, },
"author": "", "author": "",
@ -35,21 +35,13 @@
"devDependencies": { "devDependencies": {
"@eslint-sukka/node": "^4.1.7", "@eslint-sukka/node": "^4.1.7",
"@types/mocha": "10.0.2", "@types/mocha": "10.0.2",
"@types/node": "^20.9.0", "bun-types": "^1.0.11",
"chai": "4.3.10", "chai": "4.3.10",
"eslint-config-sukka": "4.1.7", "eslint-config-sukka": "4.1.7",
"eslint-formatter-sukka": "4.1.7", "eslint-formatter-sukka": "4.1.7",
"mocha": "^10.2.0" "mocha": "^10.2.0"
}, },
"engines": { "resolutions": {
"node": ">=18.0.0" "has": "npm:@nolyfill/has@latest"
},
"pnpm": {
"patchedDependencies": {
"@vercel/fetch-retry@5.1.3": "patches/@vercel__fetch-retry@5.1.3.patch"
},
"overrides": {
"has": "npm:@nolyfill/has@latest"
}
} }
} }

1772
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

18
tsconfig.json Normal file
View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"composite": true,
"target": "esnext",
"moduleDetection": "force",
"module": "esnext",
"moduleResolution": "bundler",
"types": ["bun-types"],
"allowImportingTsExtensions": true,
"allowJs": true,
"noEmit": true,
"downlevelIteration": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}