1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
// Downloads tests from Deno and does AST transformation to convert APIs
// like Deno.test() to use Bun's test() and expect() APIs.
import { mkdirSync } from "node:fs";
import { join, dirname } from "node:path";
import { parse, print } from "@swc/core";
import type { ImportDeclaration, ExpressionStatement, CallExpression } from "@swc/core";
import resources from "../resources.json";
type Test = {
path: string;
remotePath: string;
skip?: boolean;
skipTests?: string[];
};
type ParsedTest = Test & {
src: string;
testCount: number;
tests: string[];
};
async function downloadTest(test: Test): Promise<ParsedTest> {
const path = join(import.meta.dir, "..", test.path);
const url = new URL(test.remotePath, resources.baseUrl);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`${response.status}: ${url}`);
}
const src = await response.text();
if (test.skip) {
return {
...test,
tests: [],
testCount: 0,
src,
};
}
const headers = [
"// GENERATED - DO NOT EDIT",
"// Copyright 2018+ the Deno authors. All rights reserved. MIT license.",
`// ${url}`,
`import { createDenoTest } from "deno:harness";`,
];
const ast = await parse(src, {
syntax: "typescript",
target: "esnext",
dynamicImport: true,
});
let tests: string[] = [];
const harness = new Set(["test"]);
for (const item of ast.body) {
if (item.type === "ImportDeclaration") {
const found = visitImport(item);
if (found?.path === "deno:harness") {
ast.body = ast.body.filter(i => i !== item);
for (const specifier of found.specifiers) {
harness.add(specifier);
}
} else if (found) {
item.source.raw = `"${found.path}"`;
}
}
if (item.type === "ExpressionStatement") {
for (const name of visitExpression(item, test)) {
tests.push(name);
}
}
}
headers.push(`const { ${Array.from(harness).join(", ")} } = createDenoTest(import.meta.path);`);
const { code } = await print(ast, {
isModule: true,
});
const dst = [...headers, code].join("\n");
try {
mkdirSync(dirname(path));
} catch {}
await Bun.write(path, dst);
return {
...test,
tests,
testCount: tests.length,
src: dst,
};
}
type Import = {
path: string;
specifiers: string[];
};
function visitImport(item: ImportDeclaration): Import | null {
const src = item.source.value;
let dst = "";
for (const [from, to] of Object.entries(resources.imports)) {
if (src.endsWith(from)) {
dst = to;
break;
}
}
if (!dst) {
console.warn("Unknown import:", dst);
return null;
}
return {
path: dst,
specifiers: item.specifiers.map(specifier => specifier.local.value),
};
}
function* visitExpression(item: ExpressionStatement, test: Test): Generator<string> {
if (
item.expression.type === "CallExpression" &&
item.expression.callee.type === "MemberExpression" &&
item.expression.callee.object.type === "Identifier" &&
item.expression.callee.object.value === "Deno"
) {
if (item.expression.callee.property.type === "Identifier" && item.expression.callee.property.value === "test") {
yield* visitTest(item.expression, test);
item.expression.callee = item.expression.callee.property;
}
}
}
function* visitTest(item: CallExpression, test: Test): Generator<string> {
for (const argument of item.arguments) {
if (argument.expression.type === "FunctionExpression") {
const fn = argument.expression.identifier?.value;
if (fn && test.skipTests && test.skipTests.includes(fn)) {
// @ts-ignore
item.callee.property.value = "test.ignore";
}
}
}
}
for (const test of resources.tests) {
await downloadTest(test);
}
|