Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"private": true,
"scripts": {
"prepare-to-publish": "node scripts/prepare-to-publish.ts",
"sort-staged-imports": "npx @biomejs/biome check --formatter-enabled=false --linter-enabled=false --staged --write"
"sort-staged-imports": "npx @biomejs/biome check --formatter-enabled=false --linter-enabled=false --staged --write",
"test": "node --test scripts/lib.test.ts"
},
"type": "module",
"version": "0.0.0"
Expand Down
84 changes: 84 additions & 0 deletions scripts/lib.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import assert from "node:assert/strict";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { describe, it } from "node:test";
import { copyFile, loadExamples } from "./lib.ts";

describe("copyFile", () => {
it("copies a file to the destination", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "aj-test-"));
try {
const src = path.join(tmpDir, "src.txt");
const dst = path.join(tmpDir, "subdir", "dst.txt");
await fs.writeFile(src, "hello");
await copyFile(src, dst);
const content = await fs.readFile(dst, "utf-8");
assert.equal(content, "hello");
} finally {
await fs.rm(tmpDir, { recursive: true });
}
});

it("creates intermediate directories", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "aj-test-"));
try {
const src = path.join(tmpDir, "src.txt");
const dst = path.join(tmpDir, "a", "b", "c", "dst.txt");
await fs.writeFile(src, "world");
await copyFile(src, dst);
const content = await fs.readFile(dst, "utf-8");
assert.equal(content, "world");
} finally {
await fs.rm(tmpDir, { recursive: true });
}
});
});

describe("loadExamples", () => {
it("returns an empty array when examples dir is empty", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "aj-test-"));
try {
await fs.mkdir(path.join(tmpDir, "examples"));
const examples = await loadExamples(tmpDir);
assert.deepEqual(examples, []);
} finally {
await fs.rm(tmpDir, { recursive: true });
}
});

it("returns workspace tuples for each example directory", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "aj-test-"));
try {
await fs.mkdir(path.join(tmpDir, "examples", "astro"), {
recursive: true,
});
await fs.mkdir(path.join(tmpDir, "examples", "nextjs"), {
recursive: true,
});
const examples = await loadExamples(tmpDir);
assert.deepEqual(examples, [
["@arcjet-examples/astro", path.join(tmpDir, "examples", "astro")],
["@arcjet-examples/nextjs", path.join(tmpDir, "examples", "nextjs")],
]);
} finally {
await fs.rm(tmpDir, { recursive: true });
}
});

it("ignores non-directory entries", async () => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "aj-test-"));
try {
await fs.mkdir(path.join(tmpDir, "examples", "astro"), {
recursive: true,
});
await fs.writeFile(path.join(tmpDir, "examples", "file.txt"), "");
const examples = await loadExamples(tmpDir);
assert.deepEqual(examples, [
["@arcjet-examples/astro", path.join(tmpDir, "examples", "astro")],
]);
} finally {
await fs.rm(tmpDir, { recursive: true });
}
});
});
44 changes: 44 additions & 0 deletions scripts/lib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import fs from "node:fs/promises";
import path from "node:path";
import type { SimpleGit } from "simple-git";

/**
* Copies a file from one location to another, creating the necessary directories.
*/
export async function copyFile(from: string, to: string): Promise<void> {
await fs.mkdir(path.dirname(to), { recursive: true });
await fs.copyFile(from, to);
}

/**
* Lists all tracked files in a git repository at a given path.
*/
export async function listTrackedFiles(
git: SimpleGit,
filePath?: string,
): Promise<string[]> {
const filesRaw = await git.raw(
typeof filePath === "string" ? ["ls-files", filePath] : ["ls-files"],
);
return filesRaw
.split("\n")
.map((f) => f.trim())
.filter(Boolean);
}

/**
* Loads all example workspaces from the `examples/` directory.
* Returns an array of [workspaceName, workspacePath] tuples.
*/
export async function loadExamples(
baseDir: string,
): Promise<[string, string][]> {
const examplesDir = path.join(baseDir, "examples");
const entries = await fs.readdir(examplesDir, { withFileTypes: true });
return entries
.filter((entry) => entry.isDirectory())
.map((entry) => [
`@arcjet-examples/${entry.name}`,
path.join(examplesDir, entry.name),
]);
}
72 changes: 4 additions & 68 deletions scripts/prepare-to-publish.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,10 @@
import fs from "node:fs/promises";
import path from "node:path";
import { type SimpleGit, simpleGit } from "simple-git";
import { simpleGit } from "simple-git";
import { copyFile, listTrackedFiles, loadExamples } from "./lib.ts";

const BASE_PATH = path.join(import.meta.dirname, "..");

/**
* Copies a file from one location to another, creating the necessary directories.
*/
async function copyFile(from: string, to: string): Promise<void> {
await fs.mkdir(path.dirname(to), { recursive: true });
await fs.copyFile(from, to);
}

/**
* Lists all tracked files in a git repository at a given path.
*/
async function listTrackedFiles(
git: SimpleGit,
path?: string,
): Promise<string[]> {
const filesRaw = await git.raw(
typeof path === "string" ? ["ls-files", path] : ["ls-files"],
);
return filesRaw
.split("\n")
.map((f) => f.trim())
.filter(Boolean);
}

const git = simpleGit({
baseDir: BASE_PATH,
});
Expand All @@ -48,47 +25,7 @@ if (!status.isClean()) {
process.exit(1);
}

// TODO(#31): Add an improved loading mechanism for workspaces
const workspaces = [
["@arcjet-examples/astro", path.join(BASE_PATH, "./examples/astro")],
["@arcjet-examples/deno", path.join(BASE_PATH, "./examples/deno")],
["@arcjet-examples/expressjs", path.join(BASE_PATH, "./examples/expressjs")],
["@arcjet-examples/fastapi", path.join(BASE_PATH, "./examples/fastapi")],
["@arcjet-examples/fastify", path.join(BASE_PATH, "./examples/fastify")],
[
"@arcjet-examples/firebase-functions",
path.join(BASE_PATH, "./examples/firebase-functions"),
],
["@arcjet-examples/flask", path.join(BASE_PATH, "./examples/flask")],
["@arcjet-examples/nestjs", path.join(BASE_PATH, "./examples/nestjs")],
[
"@arcjet-examples/nextjs-bot-protection",
path.join(BASE_PATH, "./examples/nextjs-bot-protection"),
],
[
"@arcjet-examples/nextjs-fly",
path.join(BASE_PATH, "./examples/nextjs-fly"),
],
[
"@arcjet-examples/nextjs-form",
path.join(BASE_PATH, "./examples/nextjs-form"),
],
[
"@arcjet-examples/nextjs-server-action",
path.join(BASE_PATH, "./examples/nextjs-server-action"),
],
["@arcjet-examples/nextjs", path.join(BASE_PATH, "./examples/nextjs")],
["@arcjet-examples/nuxt", path.join(BASE_PATH, "./examples/nuxt")],
[
"@arcjet-examples/react-router",
path.join(BASE_PATH, "./examples/react-router"),
],
["@arcjet-examples/sveltekit", path.join(BASE_PATH, "./examples/sveltekit")],
[
"@arcjet-examples/tanstack-start",
path.join(BASE_PATH, "./examples/tanstack-start"),
],
] satisfies [string, string][];
const workspaces = await loadExamples(BASE_PATH);

const BUILD_PATH = path.join(BASE_PATH, "dist");

Expand Down Expand Up @@ -118,8 +55,7 @@ for (const [workspaceName, workspacePath] of workspaces) {
// Intentionally left blank
}

// Fallback to reading example.json if package.json is not found
// TODO(#31): Rework this script
// Fallback to reading package.json if arcjet-example.json is not found
if (!raw) {
raw = await fs.readFile(path.join(workspacePath, "package.json"), "utf-8");
}
Expand Down