|
| 1 | +import { mkdtemp, readdir, readFile } from "node:fs/promises" |
| 2 | +import path from "node:path" |
| 3 | +import { tmpdir } from "node:os" |
| 4 | +import { execFile as execFileCallback } from "node:child_process" |
| 5 | +import { promisify } from "node:util" |
| 6 | +import { describe, expect, it } from "vitest" |
| 7 | + |
| 8 | +const execFile = promisify(execFileCallback) |
| 9 | + |
| 10 | +async function listJsFiles(dir: string): Promise<string[]> { |
| 11 | + const entries = await readdir(dir, { withFileTypes: true }) |
| 12 | + const files = await Promise.all( |
| 13 | + entries.map(async (entry) => { |
| 14 | + const entryPath = path.join(dir, entry.name) |
| 15 | + if (entry.isDirectory()) { |
| 16 | + return listJsFiles(entryPath) |
| 17 | + } |
| 18 | + if (entry.isFile() && entry.name.endsWith(".js")) { |
| 19 | + return [entryPath] |
| 20 | + } |
| 21 | + return [] |
| 22 | + }), |
| 23 | + ) |
| 24 | + return files.flat() |
| 25 | +} |
| 26 | + |
| 27 | +describe("packed artifact", () => { |
| 28 | + it("does not ship unresolved runtime package imports beyond the OpenClaw peer", async () => { |
| 29 | + const packDir = await mkdtemp(path.join(tmpdir(), "inline-openclaw-pack-")) |
| 30 | + const extractDir = await mkdtemp(path.join(tmpdir(), "inline-openclaw-extract-")) |
| 31 | + const packageDir = path.resolve(__dirname, "..", "..") |
| 32 | + |
| 33 | + const packResult = await execFile( |
| 34 | + "npm", |
| 35 | + ["pack", "--pack-destination", packDir], |
| 36 | + { cwd: packageDir }, |
| 37 | + ) |
| 38 | + const packedFile = packResult.stdout.trim().split("\n").at(-1) |
| 39 | + expect(packedFile).toBeTruthy() |
| 40 | + |
| 41 | + const tarballPath = path.join(packDir, packedFile!) |
| 42 | + await execFile("tar", ["-xzf", tarballPath, "-C", extractDir]) |
| 43 | + |
| 44 | + const distDir = path.join(extractDir, "package", "dist") |
| 45 | + const jsFiles = await listJsFiles(distDir) |
| 46 | + expect(jsFiles.length).toBeGreaterThan(0) |
| 47 | + |
| 48 | + const unresolvedBareImports = new Set<string>() |
| 49 | + const importPattern = /from\s+"([^"]+)"/g |
| 50 | + |
| 51 | + for (const file of jsFiles) { |
| 52 | + const contents = await readFile(file, "utf8") |
| 53 | + for (const match of contents.matchAll(importPattern)) { |
| 54 | + const specifier = match[1] |
| 55 | + if ( |
| 56 | + specifier.startsWith("./") || |
| 57 | + specifier.startsWith("../") || |
| 58 | + specifier.startsWith("node:") || |
| 59 | + specifier === "openclaw/plugin-sdk" |
| 60 | + ) { |
| 61 | + continue |
| 62 | + } |
| 63 | + unresolvedBareImports.add(specifier) |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + expect([...unresolvedBareImports]).toEqual([]) |
| 68 | + }) |
| 69 | +}) |
0 commit comments