Implement multibuild support

This commit is contained in:
Simon Sawicki
2025-09-03 01:46:31 +02:00
parent c0927ec09d
commit 4d20e9a3a6
11 changed files with 1628 additions and 201 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,6 @@
/dist
*.py[cd]
/yt_dlp_jsc_deno/*.js
/node_modules
/bun.lock
/deno.lock

View File

@@ -1,18 +0,0 @@
{
"imports": {
"@std/assert": "jsr:@std/assert@1",
"@std/io": "jsr:@std/io",
"@std/fs/exists": "jsr:@std/fs/exists",
"astring": "npm:astring@1.9.0",
"meriyah": "npm:meriyah@6.1.4"
},
"test": {
"exclude": ["./dist"]
},
"tasks": {
"download": "deno run --allow-read --allow-write --allow-net=www.youtube.com tests/download.ts",
"test": "deno test --allow-read --location 'https://www.youtube.com/watch?v=yt-dlp-wins'",
"ptest": "deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} main$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} tcc$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} tce$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} es5$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} es6$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} tv$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} tv_es6$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} phone$/\" & deno task --quiet test --quiet --reporter dot --hide-stacktraces --filter \"/^[a-f\\d]{8} tablet$/\"",
"bundle": "deno bundle --output dist/jsc-deno.js src/main.ts"
}
}

53
deno.lock generated
View File

@@ -1,53 +0,0 @@
{
"version": "5",
"specifiers": {
"jsr:@std/assert@1": "1.0.14",
"jsr:@std/bytes@^1.0.5": "1.0.6",
"jsr:@std/fs@*": "1.0.18",
"jsr:@std/internal@^1.0.10": "1.0.10",
"jsr:@std/io@*": "0.225.2",
"npm:astring@1.9.0": "1.9.0",
"npm:meriyah@6.1.4": "6.1.4"
},
"jsr": {
"@std/assert@1.0.14": {
"integrity": "68d0d4a43b365abc927f45a9b85c639ea18a9fab96ad92281e493e4ed84abaa4",
"dependencies": [
"jsr:@std/internal"
]
},
"@std/bytes@1.0.6": {
"integrity": "f6ac6adbd8ccd99314045f5703e23af0a68d7f7e58364b47d2c7f408aeb5820a"
},
"@std/fs@1.0.18": {
"integrity": "24bcad99eab1af4fde75e05da6e9ed0e0dce5edb71b7e34baacf86ffe3969f3a"
},
"@std/internal@1.0.10": {
"integrity": "e3be62ce42cab0e177c27698e5d9800122f67b766a0bea6ca4867886cbde8cf7"
},
"@std/io@0.225.2": {
"integrity": "3c740cd4ee4c082e6cfc86458f47e2ab7cb353dc6234d5e9b1f91a2de5f4d6c7",
"dependencies": [
"jsr:@std/bytes"
]
}
},
"npm": {
"astring@1.9.0": {
"integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==",
"bin": true
},
"meriyah@6.1.4": {
"integrity": "sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ=="
}
},
"workspace": {
"dependencies": [
"jsr:@std/assert@1",
"jsr:@std/fs@*",
"jsr:@std/io@*",
"npm:astring@1.9.0",
"npm:meriyah@6.1.4"
]
}
}

1467
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,19 @@
{
"type": "module",
"scripts": {
"bundle": "rollup -c"
},
"dependencies": {
"astring": "1.9.0",
"meriyah": "6.1.4"
},
"devDependencies": {
"rollup": "4.49.0"
"@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-sucrase": "5.0.2",
"@rollup/plugin-terser": "0.4.4",
"@types/bun": "1.2.21",
"@types/deno": "2.3.0",
"@types/node": "24.3.0",
"rollup": "4.50.0"
}
}

86
rollup.config.js Normal file
View File

@@ -0,0 +1,86 @@
import { defineConfig } from "rollup";
import nodeResolve from "@rollup/plugin-node-resolve";
import sucrase from "@rollup/plugin-sucrase";
import terser from "@rollup/plugin-terser";
export default defineConfig([
{
input: "src/main.ts",
output: {
name: "jsc",
globals: {
astring: "astring",
input: "input",
meriyah: "meriyah",
},
file: "dist/jsc.js",
format: "iife",
},
external: ["astring", "meriyah"],
plugins: [
nodeResolve(),
sucrase({
exclude: ["node_modules/**"],
transforms: ["typescript"],
}),
],
},
{
input: "src/main.ts",
output: {
name: "jsc",
globals: {
astring: "astring",
input: "input",
meriyah: "meriyah",
},
file: "dist/jsc.min.js",
compact: true,
format: "iife",
minifyInternalExports: true,
},
external: ["astring", "meriyah"],
plugins: [
nodeResolve(),
sucrase({
exclude: ["node_modules/**"],
transforms: ["typescript"],
}),
terser(),
],
},
{
input: "src/lib.ts",
output: {
name: "lib",
file: "dist/lib.js",
format: "iife",
exports: "named",
},
plugins: [
nodeResolve(),
sucrase({
exclude: ["node_modules/**"],
transforms: ["typescript"],
}),
],
},
{
input: "src/lib.ts",
output: {
name: "lib",
file: "dist/lib.min.js",
compact: true,
format: "iife",
minifyInternalExports: true,
},
plugins: [
nodeResolve(),
sucrase({
exclude: ["node_modules/**"],
transforms: ["typescript"],
}),
terser(),
],
},
]);

5
src/lib.ts Normal file
View File

@@ -0,0 +1,5 @@
import { parse } from "meriyah";
import { generate } from "astring";
export const meriyah = { parse };
export const astring = { generate };

View File

@@ -1,13 +1,10 @@
import { read, write } from "./io.ts";
import { getFromPrepared, preprocessPlayer } from "./solvers.ts";
import { isOneOf } from "./utils.ts";
function main(input: Input): Output {
const preprocessedPlayer =
input.type === "player"
? preprocessPlayer(input.player)
: input.preprocessed_player;
export default function main(input: Input): Output {
const preprocessedPlayer = input.type === "player"
? preprocessPlayer(input.player)
: input.preprocessed_player;
const solvers = getFromPrepared(preprocessedPlayer);
const responses = input.requests.map(
@@ -37,13 +34,12 @@ function main(input: Input): Output {
return {
type: "error",
request,
error:
error instanceof Error
? `${error.message}\n${error.stack}`
: `${error}`,
error: error instanceof Error
? `${error.message}\n${error.stack}`
: `${error}`,
};
}
}
},
);
const output: Output = {
@@ -56,33 +52,18 @@ function main(input: Input): Output {
return output;
}
async function safeMain(): Promise<void> {
try {
const input = await read();
const output = main(input);
await write(output);
} catch (error) {
await write({
type: "error",
error: `${error}`,
});
}
}
safeMain();
export type Input =
| {
type: "player";
player: string;
requests: JsChallengeRequest[];
output_preprocessed: boolean;
}
type: "player";
player: string;
requests: JsChallengeRequest[];
output_preprocessed: boolean;
}
| {
type: "preprocessed";
preprocessed_player: string;
requests: JsChallengeRequest[];
};
type: "preprocessed";
preprocessed_player: string;
requests: JsChallengeRequest[];
};
type JsChallengeRequest = {
type: string;
@@ -93,23 +74,23 @@ type JsChallengeRequest = {
type JsChallengeProviderResponse =
| {
type: "result";
request: JsChallengeRequest;
response: string;
}
type: "result";
request: JsChallengeRequest;
response: string;
}
| {
type: "error";
request: JsChallengeRequest;
error: string;
};
type: "error";
request: JsChallengeRequest;
error: string;
};
export type Output =
| {
type: "result";
preprocessed_player?: string;
responses: JsChallengeProviderResponse[];
}
type: "result";
preprocessed_player?: string;
responses: JsChallengeProviderResponse[];
}
| {
type: "error";
error: string;
};
type: "error";
error: string;
};

View File

@@ -16,9 +16,9 @@ for (const test of tests) {
await subtest(`${step.input} (${mode})`, () => {
const got = solvers[mode]?.(step.input);
assert.equal(got, step.expected);
})
});
}
}
})
});
}
}

View File

@@ -97,8 +97,7 @@ export function preprocessPlayer(data: string): string {
ast.body.splice(0, 0, ...setupNodes);
const code = generate(ast);
return code;
return generate(ast);
}
export function getFromPrepared(code: string): {

View File

@@ -23,27 +23,17 @@ export async function getIO(): Promise<IO> {
}
async function _getIO(): Promise<IO> {
if (typeof Deno !== "undefined") {
const { exists } = await import("@std/fs/exists");
const { assertStrictEquals } = await import("@std/assert");
const assert = {
equal<T>(actual: T, expected: T, message?: string) {
return assertStrictEquals(actual, expected, message);
},
if (globalThis.process?.release?.name === "node") {
// Assume node compatibility
const { readFile, writeFile, access } = await import("node:fs/promises");
const { deepStrictEqual } = await import("node:assert");
const assert: Assert = {
equal: deepStrictEqual,
};
return {
exists,
read(path: string): Promise<string> {
return Deno.readTextFile(path);
},
async write(path: string, response: Response): Promise<void> {
const file = await Deno.open(path, {
createNew: true,
write: true,
});
response.body!.pipeTo(file.writable);
},
test(name: string, func: TestFunc) {
let test: Test;
if (typeof globalThis.Deno !== "undefined") {
// deno does not like `node:test` for some reason
test = (name, func) => {
Deno.test(name, (t) => {
return func(assert, async (name, func): Promise<void> => {
await t.step(name, () => {
@@ -52,53 +42,20 @@ async function _getIO(): Promise<IO> {
});
});
return Promise.resolve();
},
};
} else if (typeof Bun !== "undefined") {
const { expect, test } = await import("bun:test");
const { access } = await import("node:fs/promises");
const assert = {
equal<T>(actual: T, expected: T, message?: string) {
return expect(actual).toBe(expected, message);
},
};
return {
async exists(path: string): Promise<boolean> {
try {
await access(path);
return true;
} catch {
return false;
}
},
read(path: string): Promise<string> {
return Bun.file(path).text();
},
write(path: string, response: Response): Promise<void> {
return Bun.write(path, response);
},
test(name: string, func: TestFunc) {
test(name, () => {
// XXX: how to do subtests
};
} else {
const { suite, test: subtest } = await import("node:test");
test = (name, func) => {
suite(name, () => {
return func(assert, async (name, func): Promise<void> => {
await func(assert);
await subtest(name, async () => {
await func(assert);
});
});
});
return Promise.resolve();
},
};
} else if (
typeof navigator === "object" &&
navigator.userAgent.startsWith("Node.js")
) {
const { suite, test } = await import("node:test");
const { readFile, writeFile, access } = await import("node:fs/promises");
const { deepStrictEqual } = await import("node:assert");
const assert: Assert = {
equal<T>(actual: T, expected: T, message?: string): void {
deepStrictEqual(actual, expected, message);
},
};
};
}
return {
async exists(path: string): Promise<boolean> {
try {
@@ -114,21 +71,12 @@ async function _getIO(): Promise<IO> {
write(path: string, response: Response): Promise<void> {
return writeFile(path, response.body!);
},
test(name: string, func: TestFunc): Promise<void> {
suite(name, () => {
return func(assert, async (name, func): Promise<void> => {
await test(name, async () => {
await func(assert);
});
});
});
return Promise.resolve();
},
test,
};
}
throw new Error(
`unsupported runtime for testing${
navigator.userAgent ? `: ${navigator.userAgent}` : ""
}`
}`,
);
}