feat: import Chinese-localized Buzz source snapshot
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Signed-off-by: cls_宁波本机 <908705107@qq.com>
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import ts from "typescript";
|
||||
|
||||
const srcRoot = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"src",
|
||||
);
|
||||
|
||||
const repoRoot = path.resolve(
|
||||
path.dirname(fileURLToPath(import.meta.url)),
|
||||
"..",
|
||||
);
|
||||
|
||||
// `nextResolve` requires specifiers to be URLs or relative paths. Passing an
|
||||
// absolute filesystem path happens to work on POSIX (node coerces it), but on
|
||||
// Windows an absolute path like `C:\...` parses as a URL with protocol `c:`
|
||||
// and every test run dies with ERR_UNSUPPORTED_ESM_URL_SCHEME. Hand absolute
|
||||
// paths to node as proper file:// URLs on all platforms.
|
||||
function toFileSpecifier(candidatePath) {
|
||||
return path.isAbsolute(candidatePath)
|
||||
? pathToFileURL(candidatePath).href
|
||||
: candidatePath;
|
||||
}
|
||||
|
||||
function resolveSourcePath(basePath) {
|
||||
// Existence decides, not path.extname — a dotted basename like
|
||||
// `ProfileAvatarEditor.utils` (→ .utils.ts on disk) looks like an
|
||||
// extension but still needs resolving.
|
||||
if (fs.existsSync(basePath) && fs.statSync(basePath).isFile()) {
|
||||
return basePath;
|
||||
}
|
||||
|
||||
for (const extension of [".ts", ".tsx", ".js", ".jsx", ".mjs"]) {
|
||||
const candidate = `${basePath}${extension}`;
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
for (const extension of [".ts", ".tsx", ".js", ".jsx", ".mjs"]) {
|
||||
const candidate = path.join(basePath, `index${extension}`);
|
||||
if (fs.existsSync(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// emoji-mart ships a bundled CJS main that node's cjs-module-lexer cannot
|
||||
// extract named exports from (`import { init } from "emoji-mart"` throws
|
||||
// under node ESM even though the bundler handles it). Tests never exercise
|
||||
// the picker, so serve inert stubs for the emoji-mart entrypoints.
|
||||
const stubModules = new Map([
|
||||
[
|
||||
"emoji-mart",
|
||||
"export const init = () => {};\n" +
|
||||
"export const SearchIndex = { search: async () => [] };\n" +
|
||||
"export default {};\n",
|
||||
],
|
||||
["@emoji-mart/react", "export default function Picker() { return null; }\n"],
|
||||
]);
|
||||
|
||||
const STUB_URL_PREFIX = "buzz-test-stub:";
|
||||
|
||||
// Vite resolves asset imports (`./logo.png`, `./logo.png?inline`) to a URL or
|
||||
// base64 string at bundle time; node's ESM resolver has no such loader and
|
||||
// throws on the query suffix. Serve an inert string so components that embed
|
||||
// assets stay unit-testable.
|
||||
const ASSET_SPECIFIER = /\.(?:png|jpe?g|gif|svg|webp|avif|ico)(?:\?[^/]*)?$/;
|
||||
const ASSET_URL_PREFIX = "buzz-test-asset:";
|
||||
|
||||
export function resolve(specifier, context, nextResolve) {
|
||||
if (ASSET_SPECIFIER.test(specifier)) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `${ASSET_URL_PREFIX}${specifier}`,
|
||||
};
|
||||
}
|
||||
if (stubModules.has(specifier)) {
|
||||
return {
|
||||
shortCircuit: true,
|
||||
url: `${STUB_URL_PREFIX}${specifier}`,
|
||||
};
|
||||
}
|
||||
if (specifier === "@features-manifest") {
|
||||
const resolved = path.join(repoRoot, "preview-features.json");
|
||||
return nextResolve(toFileSpecifier(resolved), context);
|
||||
}
|
||||
if (specifier.startsWith("@/")) {
|
||||
const stripped = specifier.slice(2);
|
||||
// Preserve explicit extensions (.mjs, .js, .json, .ts, etc.). The bundler
|
||||
// tolerates extensionless `@/` imports for source files; node's ESM
|
||||
// resolver does not, so resolve against the extensions the app uses.
|
||||
// Otherwise paths like `@/.../foo.mjs` would be coerced into `foo.mjs.ts`
|
||||
// and fail to resolve.
|
||||
const resolved = resolveSourcePath(`${srcRoot}/${stripped}`);
|
||||
return nextResolve(
|
||||
toFileSpecifier(resolved ?? `${srcRoot}/${stripped}`),
|
||||
context,
|
||||
);
|
||||
}
|
||||
// Resolve extensionless relative TS imports (e.g. `./parseImeta`) — the app's
|
||||
// bundler adds the extension, but node's ESM resolver does not. Without this,
|
||||
// any .ts that relative-imports a sibling .ts can't be imported from a test,
|
||||
// which previously forced stale inlined copies of the source under test.
|
||||
// Dotted basenames (`./ProfileAvatarEditor.utils`) look like extensions to
|
||||
// path.extname, so resolveSourcePath existence-checks instead.
|
||||
if (
|
||||
(specifier.startsWith("./") || specifier.startsWith("../")) &&
|
||||
context.parentURL?.startsWith("file:")
|
||||
) {
|
||||
const parentPath = fileURLToPath(context.parentURL);
|
||||
const resolved = resolveSourcePath(
|
||||
path.resolve(path.dirname(parentPath), specifier),
|
||||
);
|
||||
if (resolved) {
|
||||
return nextResolve(toFileSpecifier(resolved), context);
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
}
|
||||
|
||||
export async function load(url, context, nextLoad) {
|
||||
if (url.startsWith(ASSET_URL_PREFIX)) {
|
||||
return {
|
||||
format: "module",
|
||||
shortCircuit: true,
|
||||
source: 'export default "test-asset";\n',
|
||||
};
|
||||
}
|
||||
|
||||
if (url.startsWith(STUB_URL_PREFIX)) {
|
||||
return {
|
||||
format: "module",
|
||||
shortCircuit: true,
|
||||
source: stubModules.get(url.slice(STUB_URL_PREFIX.length)) ?? "",
|
||||
};
|
||||
}
|
||||
|
||||
// The app bundler loads .json imports without attributes (e.g. the bare
|
||||
// `@emoji-mart/data` entrypoint); node's ESM resolver requires
|
||||
// `with { type: "json" }` on every hop. Serve json here so transitive
|
||||
// imports from source under test don't need bundler-only semantics.
|
||||
if (url.endsWith(".json")) {
|
||||
return {
|
||||
format: "json",
|
||||
shortCircuit: true,
|
||||
source: fs.readFileSync(fileURLToPath(url), "utf8"),
|
||||
};
|
||||
}
|
||||
|
||||
// Vite handles side-effect CSS imports (e.g. `import "./card-texture.css"`
|
||||
// in shared/ui) at bundle time; node's ESM loader has no CSS support. Serve
|
||||
// them as empty modules so components with style imports stay unit-testable.
|
||||
if (url.endsWith(".css")) {
|
||||
return {
|
||||
format: "module",
|
||||
shortCircuit: true,
|
||||
source: "",
|
||||
};
|
||||
}
|
||||
|
||||
if (url.endsWith(".tsx")) {
|
||||
const source = fs.readFileSync(fileURLToPath(url), "utf8");
|
||||
const transpiled = ts.transpileModule(source, {
|
||||
compilerOptions: {
|
||||
jsx: ts.JsxEmit.ReactJSX,
|
||||
module: ts.ModuleKind.ESNext,
|
||||
target: ts.ScriptTarget.ES2020,
|
||||
},
|
||||
fileName: fileURLToPath(url),
|
||||
});
|
||||
|
||||
return {
|
||||
format: "module",
|
||||
shortCircuit: true,
|
||||
source: transpiled.outputText,
|
||||
};
|
||||
}
|
||||
|
||||
return nextLoad(url, context);
|
||||
}
|
||||
Reference in New Issue
Block a user