Skip to content

Commit

Permalink
refactor plugin-zip
Browse files Browse the repository at this point in the history
  • Loading branch information
tasshi-me committed Oct 25, 2024
1 parent 20c9f66 commit e12c6c4
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 90 deletions.
49 changes: 29 additions & 20 deletions src/plugin/packer/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,25 @@ import { globSync } from "glob";
import cli from "../cli";
import { logger } from "../../../utils/log";
import type { ContentsZipInterface } from "../contents-zip";
import type { PluginZipInterface } from "../plugin-zip";
import { ZipFile } from "../zip";

type MockedPacker = jest.MockedFunction<
(
contentsZip: ContentsZipInterface,
privateKey_?: string,
) => Promise<{ plugin: PluginZipInterface; privateKey: string; id: string }>
>;

class MockPluginZip extends ZipFile implements PluginZipInterface {}

const fixturesDir = path.posix.join(__dirname, "fixtures");
const sampleDir = path.posix.join(fixturesDir, "sample-plugin");
const ppkPath = path.posix.join(fixturesDir, "private.ppk");

const ID = "aaa";
const PRIVATE_KEY = "PRIVATE_KEY";
const PLUGIN_BUFFER = Buffer.from("foo");

type MockedPacker = jest.MockedFunction<
(
contentsZip: ContentsZipInterface,
privateKey_?: string,
) => Promise<{ plugin: Buffer; privateKey: string; id: string }>
>;
const PLUGIN_ZIP = new MockPluginZip(Buffer.from("foo"));

describe("cli", () => {
beforeEach(() => {
Expand All @@ -36,10 +40,11 @@ describe("cli", () => {
describe("validation", () => {
let packer: MockedPacker;
beforeEach(() => {
packer = jest.fn().mockReturnValue({
packer = jest.fn();
packer.mockResolvedValue({
id: ID,
privateKey: PRIVATE_KEY,
plugin: PLUGIN_BUFFER,
plugin: PLUGIN_ZIP,
});
});

Expand Down Expand Up @@ -95,10 +100,11 @@ describe("cli", () => {
let resultPluginPath: string;

beforeEach(async () => {
packer = jest.fn().mockReturnValue({
packer = jest.fn();
packer.mockResolvedValue({
id: ID,
privateKey: PRIVATE_KEY,
plugin: PLUGIN_BUFFER,
plugin: PLUGIN_ZIP,
});

// TODO: use os tempdir
Expand Down Expand Up @@ -131,18 +137,19 @@ describe("cli", () => {

it("generates a plugin file", () => {
const pluginBuffer = fs.readFileSync(resultPluginPath);
expect(PLUGIN_BUFFER.equals(pluginBuffer)).toBe(true);
expect(PLUGIN_ZIP.buffer.equals(pluginBuffer)).toBe(true);
});
});

describe("with ppk", () => {
const pluginDir = path.join(sampleDir, "plugin-dir");
let packer: MockedPacker;
beforeEach(async () => {
packer = jest.fn().mockReturnValue({
packer = jest.fn();
packer.mockResolvedValue({
id: ID,
privateKey: PRIVATE_KEY,
plugin: PLUGIN_BUFFER,
plugin: PLUGIN_ZIP,
});

await rimraf(`${sampleDir}/*.*(ppk|zip)`, { glob: true });
Expand All @@ -163,10 +170,11 @@ describe("cli", () => {

it("includes files listed in manifest.json only", async () => {
const pluginDir = path.join(fixturesDir, "plugin-full-manifest");
const packer: MockedPacker = jest.fn().mockReturnValue({
const packer: MockedPacker = jest.fn();
packer.mockResolvedValue({
id: ID,
privateKey: PRIVATE_KEY,
plugin: PLUGIN_BUFFER,
plugin: PLUGIN_ZIP,
});

await rimraf(`${sampleDir}/*.*(ppk|zip)`, { glob: true });
Expand All @@ -191,10 +199,11 @@ describe("cli", () => {
const pluginDir = path.join(sampleDir, "plugin-dir");
const outputDir = path.join("test", ".output");
const outputPluginPath = path.join(outputDir, "foo.zip");
const packer = jest.fn().mockReturnValue({
const packer: MockedPacker = jest.fn();
packer.mockResolvedValue({
id: ID,
privateKey: PRIVATE_KEY,
plugin: PLUGIN_BUFFER,
plugin: PLUGIN_ZIP,
});

await rimraf(outputDir);
Expand All @@ -205,7 +214,7 @@ describe("cli", () => {

expect(resultPluginPath).toBe(outputPluginPath);
const pluginBuffer = fs.readFileSync(outputPluginPath);
expect(PLUGIN_BUFFER.equals(pluginBuffer)).toBe(true);
expect(PLUGIN_ZIP.buffer.equals(pluginBuffer)).toBe(true);
const ppk = fs.readFileSync(path.join(outputDir, `${ID}.ppk`));
expect(PRIVATE_KEY).toBe(ppk.toString());

Expand Down
5 changes: 2 additions & 3 deletions src/plugin/packer/__tests__/pack-plugin-from-manifest.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import path from "path";
import fs from "fs";
import { readZipContentsNames } from "./helpers/zip";
import packer from "../index";
import { packPluginFromManifest } from "../pack-plugin-from-manifest";
import { ManifestV1 } from "../manifest";
Expand All @@ -24,10 +23,10 @@ describe("pack-plugin-from-manifest", () => {
const result2 = await packer(contentsZip, privateKey);

expect(result1.id).toBe(result2.id);
expect(result1.plugin.length).toBe(result2.plugin.length);
expect(result1.plugin.buffer.length).toBe(result2.plugin.buffer.length);
expect(result1.privateKey).toBe(result2.privateKey);

const files = await readZipContentsNames(result1.plugin);
const files = await result1.plugin.fileList();
expect(files).toStrictEqual(["contents.zip", "PUBKEY", "SIGNATURE"]);
});
});
9 changes: 4 additions & 5 deletions src/plugin/packer/__tests__/packer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import fs from "fs";
import RSA from "node-rsa";
import yauzl from "yauzl";

import { readZipContentsNames } from "./helpers/zip";

import packer from "../index";
import { ContentsZip } from "../contents-zip";
import type { PluginZipInterface } from "../plugin-zip";

const privateKeyPath = path.join(__dirname, "fixtures", "private.ppk");
const contentsZipPath = path.join(__dirname, "fixtures", "contents.zip");
Expand Down Expand Up @@ -44,7 +43,7 @@ describe("packer", () => {
});

it("the zip contains 3 files", async () => {
const files = await readZipContentsNames(output.plugin);
const files = await output.plugin.fileList();
expect(files.sort()).toStrictEqual(
["contents.zip", "PUBKEY", "SIGNATURE"].sort(),
);
Expand Down Expand Up @@ -123,7 +122,7 @@ const readZipContents = (
});
};

const verifyPlugin = async (plugin: Buffer): Promise<void> => {
const verifyPlugin = async (plugin: PluginZipInterface): Promise<void> => {
const fromBuffer = (buffer: Buffer) =>
new Promise<yauzl.ZipFile>((resolve, reject) => {
yauzl.fromBuffer(buffer, (err, zipfile) => {
Expand All @@ -133,7 +132,7 @@ const verifyPlugin = async (plugin: Buffer): Promise<void> => {
resolve(zipfile);
});
});
const zipEntry = await fromBuffer(plugin);
const zipEntry = await fromBuffer(plugin.buffer);
const zipContentsMap = await readZipContents(zipEntry);
const contentZip = zipContentsMap.get("contents.zip");
expect(contentZip).toBeDefined();
Expand Down
7 changes: 5 additions & 2 deletions src/plugin/packer/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { logger } from "../../utils/log";
import { ManifestV1 } from "./manifest";
import { generateErrorMessages } from "./manifest/validate";
import { ContentsZip } from "./contents-zip";
import type { PluginZipInterface } from "./plugin-zip";

const debug = _debug("cli");
const writeFile = promisify(fs.writeFile);
Expand All @@ -21,6 +22,8 @@ type Options = Partial<{
packerMock_: typeof packer;
}>;

// TODO: Reduce statements in this func
// eslint-disable-next-line max-statements
const cli = async (pluginDir: string, options_?: Options) => {
const options = options_ || {};
const packerLocal = options.packerMock_ ? options.packerMock_ : packer;
Expand Down Expand Up @@ -130,9 +133,9 @@ export = cli;
*/
const outputPlugin = async (
outputPath: string,
plugin: Buffer,
plugin: PluginZipInterface,
): Promise<string> => {
await writeFile(outputPath, plugin);
await writeFile(outputPath, plugin.buffer);
return outputPath;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import path from "path";
import { readZipContentsNames } from "../../__tests__/helpers/zip";
import { createContentsZip } from "../create-contents-zip";
import { ManifestV1 } from "../../manifest";
import { ContentsZip } from "../index";

const fixturesDir = path.join(__dirname, "fixtures");
const pluginDir = path.join(fixturesDir, "sample-plugin", "plugin-dir");

describe("create-contents-zip", () => {
it("should be able to create buffer from a plugin directory", async () => {
describe("ContentsZip", () => {
it("should be able to create ContentsZip from a plugin directory", async () => {
const manifestJSONPath = path.join(pluginDir, "manifest.json");
const manifest = await ManifestV1.loadJsonFile(manifestJSONPath);

const buffer = await createContentsZip(pluginDir, manifest);
const files = await readZipContentsNames(buffer as Buffer);
const contentsZip = await ContentsZip.createFromManifest(
pluginDir,
manifest,
);
const files = await contentsZip.fileList();
expect(files).toStrictEqual(["manifest.json", "image/icon.png"]);
expect(buffer).toBeInstanceOf(Buffer);
expect(contentsZip).toBeInstanceOf(ContentsZip);
expect(contentsZip.buffer).toBeInstanceOf(Buffer);
});
});
32 changes: 0 additions & 32 deletions src/plugin/packer/contents-zip/create-contents-zip.ts

This file was deleted.

51 changes: 36 additions & 15 deletions src/plugin/packer/contents-zip/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { readZipContentsNames } from "../__tests__/helpers/zip";
import { createContentsZip } from "./create-contents-zip";
import type { ManifestInterface } from "../manifest";
import { validateContentsZip } from "./zip";
import { ZipFile } from "../zip";
import streamBuffers from "stream-buffers";
import path from "path";
import yazl from "yazl";

import _debug from "debug";

const debug = _debug("contents-zip");

export interface ContentsZipInterface {
fileList(): Promise<string[]>;
get buffer(): Buffer;
}

export class ContentsZip implements ContentsZipInterface {
private readonly zip: Buffer;

private constructor(zipFile: Buffer) {
this.zip = zipFile;
export class ContentsZip extends ZipFile implements ContentsZipInterface {
private constructor(buffer: Buffer) {
super(buffer);
}

public static async createFromManifest(
Expand All @@ -28,12 +32,29 @@ export class ContentsZip implements ContentsZipInterface {
await validateContentsZip(buffer);
return new ContentsZip(buffer);
}

async fileList(): Promise<string[]> {
return readZipContentsNames(this.zip);
}

public get buffer() {
return this.zip;
}
}

/**
* Create a zipped contents
*/
export const createContentsZip = async (
pluginDir: string,
manifest: ManifestInterface,
): Promise<Buffer> => {
return new Promise((res) => {
const output = new streamBuffers.WritableStreamBuffer();
const zipFile = new yazl.ZipFile();
let size: any = null;
output.on("finish", () => {
debug(`plugin.zip: ${size} bytes`);
res(output.getContents() as any);
});
zipFile.outputStream.pipe(output);
manifest.sourceList().forEach((src) => {
zipFile.addFile(path.join(pluginDir, src), src);
});
zipFile.end(undefined, ((finalSize: number) => {
size = finalSize;
}) as any);
});
};
7 changes: 4 additions & 3 deletions src/plugin/packer/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import _debug from "debug";
import { PrivateKey } from "./crypto";
import { zip } from "./plugin-zip";
import type { PluginZipInterface } from "./plugin-zip";
import { PluginZip } from "./plugin-zip";
import type { ContentsZipInterface } from "./contents-zip";

const debug = _debug("packer");
Expand All @@ -9,7 +10,7 @@ const packer = async (
contentsZip: ContentsZipInterface,
privateKey_?: string,
): Promise<{
plugin: Buffer;
plugin: PluginZipInterface;
privateKey: string;
id: string;
}> => {
Expand All @@ -26,7 +27,7 @@ const packer = async (
const id = key.uuid();
debug(`id : ${id}`);

const plugin = await zip(contentsZip, key);
const plugin = await PluginZip.build(contentsZip, key);
return {
plugin,
privateKey,
Expand Down
3 changes: 2 additions & 1 deletion src/plugin/packer/pack-plugin-from-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import path from "path";
import packer from "./index";
import { ManifestV1 } from "./manifest";
import { ContentsZip } from "./contents-zip";
import type { PluginZip } from "./plugin-zip";

export const packPluginFromManifest = async (
manifestJSONPath: string,
privateKey: string,
): Promise<{ plugin: Buffer; privateKey: string; id: string }> => {
): Promise<{ plugin: PluginZip; privateKey: string; id: string }> => {
const manifest = await ManifestV1.loadJsonFile(manifestJSONPath);
const contentsZip = await ContentsZip.createFromManifest(
path.dirname(manifestJSONPath),
Expand Down
Loading

0 comments on commit e12c6c4

Please sign in to comment.