mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-21 16:52:40 +00:00
* feat(plugins): unify source installation and simplify plugin settings * feat(plugins): install npm sources and review CLI updates Treat install selectors as one-time choices. Keep source identity separate from the installed revision, and resolve ordinary updates from the current upstream default. npm only acquires directories; plugin loading remains independent of npm. * fix(plugins): preserve CLI diagnostics and exercise reviewed updates Move new source capability coverage to real browser journeys. Update the requirements regression to exercise preview and apply, and preserve the existing duplicate-selector diagnostic across positional option scopes. * test(plugins): remove unused pending helper * test(plugins): pack registry fixtures with the Windows command runner * test(plugins): assert failed client contribution setup * docs(plugins): consolidate guides and document npm publishing * docs(plugins): introduce publishing choices before npm instructions * test(plugins): own browser journey cleanup in a fixture * feat(plugins): prepare scaffolded packages for publishing
165 lines
5.7 KiB
TypeScript
165 lines
5.7 KiB
TypeScript
import { execCommand } from "../../packages/server/src/utils/spawn.js";
|
|
import { createHash } from "node:crypto";
|
|
import { createServer } from "node:http";
|
|
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
|
|
interface FixturePackage {
|
|
name: string;
|
|
version: string;
|
|
files: Record<string, string>;
|
|
dependencies?: Record<string, string>;
|
|
peerDependencies?: Record<string, string>;
|
|
scripts?: Record<string, string>;
|
|
tags?: string[];
|
|
}
|
|
|
|
/** A registry serving real npm-packed artifacts, including integrity and host auth configuration. */
|
|
export async function startNpmRegistry(packages: FixturePackage[]) {
|
|
const root = await mkdtemp(path.join(tmpdir(), "paseo-npm-registry-"));
|
|
const artifacts = new Map<string, Buffer>();
|
|
const manifests = new Map<
|
|
string,
|
|
{ name: string; versions: Record<string, unknown>; "dist-tags": Record<string, string> }
|
|
>();
|
|
const requests: string[] = [];
|
|
const server = createServer((request, response) => {
|
|
const url = decodeURIComponent(request.url ?? "");
|
|
requests.push(url);
|
|
if (request.headers.authorization !== "Bearer fixture-token") {
|
|
response.writeHead(401).end(JSON.stringify({ error: "Registry token required" }));
|
|
return;
|
|
}
|
|
const artifact = artifacts.get(url);
|
|
if (artifact) {
|
|
response.writeHead(200, { "content-type": "application/octet-stream" }).end(artifact);
|
|
return;
|
|
}
|
|
const manifest = manifests.get(url.slice(1));
|
|
response.writeHead(manifest ? 200 : 404, { "content-type": "application/json" });
|
|
response.end(JSON.stringify(manifest ?? { error: "Package not found" }));
|
|
});
|
|
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
const address = server.address();
|
|
if (!address || typeof address === "string") throw new Error("Expected registry port");
|
|
const url = `http://127.0.0.1:${address.port}`;
|
|
const userconfig = path.join(root, "npmrc");
|
|
await writeFile(
|
|
userconfig,
|
|
`registry=${url}/\n//127.0.0.1:${address.port}/:_authToken=fixture-token\ncache=${root}/cache\n`,
|
|
);
|
|
try {
|
|
for (const [index, pkg] of packages.entries()) {
|
|
const directory = path.join(root, String(index));
|
|
await mkdir(directory);
|
|
const manifest = {
|
|
name: pkg.name,
|
|
version: pkg.version,
|
|
dependencies: pkg.dependencies,
|
|
peerDependencies: pkg.peerDependencies,
|
|
scripts: pkg.scripts,
|
|
};
|
|
await writeFile(path.join(directory, "package.json"), JSON.stringify(manifest));
|
|
for (const [file, content] of Object.entries(pkg.files)) {
|
|
await mkdir(path.dirname(path.join(directory, file)), { recursive: true });
|
|
await writeFile(path.join(directory, file), content);
|
|
}
|
|
const { stdout } = await execCommand("npm", ["pack", "--ignore-scripts", "--json"], {
|
|
cwd: directory,
|
|
});
|
|
const packed: Array<{ filename: string }> = JSON.parse(stdout);
|
|
const artifact = await readFile(path.join(directory, packed[0].filename));
|
|
const artifactPath = `/artifacts/${index}.tgz`;
|
|
artifacts.set(artifactPath, artifact);
|
|
const published = {
|
|
...manifest,
|
|
dist: {
|
|
tarball: `${url}${artifactPath}`,
|
|
integrity: `sha512-${createHash("sha512").update(artifact).digest("base64")}`,
|
|
},
|
|
};
|
|
const existing = manifests.get(pkg.name) ?? { name: pkg.name, versions: {}, "dist-tags": {} };
|
|
const versions = existing.versions;
|
|
const tags = existing["dist-tags"];
|
|
versions[pkg.version] = published;
|
|
for (const tag of pkg.tags ?? ["latest"]) tags[tag] = pkg.version;
|
|
manifests.set(pkg.name, existing);
|
|
}
|
|
} catch (error) {
|
|
server.closeAllConnections();
|
|
server.close();
|
|
await rm(root, { recursive: true, force: true });
|
|
throw error;
|
|
}
|
|
return {
|
|
url,
|
|
userconfig,
|
|
requests,
|
|
env: { npm_config_userconfig: userconfig },
|
|
setTag(packageName: string, tag: string, version: string) {
|
|
const manifest = manifests.get(packageName);
|
|
if (!manifest?.versions[version])
|
|
throw new Error(`Unknown fixture package: ${packageName}@${version}`);
|
|
manifest["dist-tags"][tag] = version;
|
|
},
|
|
async close() {
|
|
server.closeAllConnections();
|
|
await new Promise<void>((resolve, reject) => {
|
|
server.close((error) => {
|
|
if (error) {
|
|
reject(error);
|
|
return;
|
|
}
|
|
resolve();
|
|
});
|
|
});
|
|
await rm(root, { recursive: true, force: true });
|
|
},
|
|
};
|
|
}
|
|
|
|
export function npmPluginPackages() {
|
|
const files = {
|
|
"paseo-plugin.json": JSON.stringify({
|
|
id: "npm-review",
|
|
description: "Installed from the npm fixture registry",
|
|
requirements: { paseo: ">=0.4.0" },
|
|
}),
|
|
"index.server.ts": `import value from "paseo-fixture-dependency";
|
|
export default function contribute() {
|
|
if (value !== "dependency loaded") throw new Error("Dependency did not resolve");
|
|
console.log(value);
|
|
return () => {};
|
|
}`,
|
|
};
|
|
return [
|
|
{
|
|
name: "paseo-fixture-dependency",
|
|
version: "1.0.0",
|
|
files: { "index.js": 'module.exports = "dependency loaded";' },
|
|
},
|
|
{
|
|
name: "paseo-fixture-plugin",
|
|
version: "1.0.0",
|
|
files,
|
|
dependencies: { "paseo-fixture-dependency": "1.0.0" },
|
|
tags: ["stable"],
|
|
},
|
|
{
|
|
name: "paseo-fixture-plugin",
|
|
version: "1.1.0",
|
|
files,
|
|
dependencies: { "paseo-fixture-dependency": "1.0.0" },
|
|
tags: ["latest", "next"],
|
|
},
|
|
{
|
|
name: "@paseo-fixture/review",
|
|
version: "2.0.0",
|
|
files,
|
|
dependencies: { "paseo-fixture-dependency": "1.0.0" },
|
|
peerDependencies: { "@getpaseo/plugin": "*" },
|
|
},
|
|
];
|
|
}
|