Skip to content

Commit f94de59

Browse files
committed
Tighten init review follow-ups
The init smoke tests now skip Node.js and Bun dev tool script checks when dependencies were not installed, which avoids treating missing node_modules as a formatter or linter failure. JSONC config merging now treats empty or comment-only files as empty objects while keeping other parse errors intact. The Astro tutorial also reflects that generated Astro projects use Prettier for formatting rather than an Oxfmt config file. fedify-dev#818 (comment) fedify-dev#818 (comment) fedify-dev#818 (comment) Assisted-by: Codex:gpt-5.5
1 parent ff87d44 commit f94de59

4 files changed

Lines changed: 31 additions & 4 deletions

File tree

‎docs/tutorial/astro-blog.md‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,8 @@ After a moment, you'll have a working project with the following structure:
198198
- *middleware.ts* — Connects Fedify to Astro's request pipeline
199199
- *public/* — Files served as-is (favicon, etc.)
200200
- *astro.config.ts* — Astro configuration
201-
- *.oxfmtrc.json* — Code formatter settings
202201
- *.oxlintrc.json* — Code linter settings
203-
- *package.json* — Package metadata and scripts
202+
- *package.json* — Package metadata, scripts, and Prettier formatter setup
204203
- *tsconfig.json* — TypeScript settings
205204

206205
Because we're using TypeScript instead of plain JavaScript, source files have

‎packages/init/src/action/patch.test.ts‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import assert from "node:assert/strict";
2-
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
2+
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
33
import { tmpdir } from "node:os";
44
import { join } from "node:path";
55
import test from "node:test";
@@ -9,6 +9,7 @@ import {
99
assertNoGeneratedFileConflicts,
1010
GeneratedFileConflictError,
1111
getJsonsCacheKey,
12+
patchFiles,
1213
} from "./patch.ts";
1314

1415
test("assertNoGeneratedFileConflicts allows unrelated files", async () => {
@@ -61,6 +62,19 @@ test("getJsonsCacheKey stays stable across pipeline clones", () => {
6162
assert.equal(getJsonsCacheKey(cloned), getJsonsCacheKey(data));
6263
});
6364

65+
test("patchFiles merges JSONC files containing only comments", async () => {
66+
await withTempDir(async (dir) => {
67+
await writeFile(join(dir, "package.json"), "// generated scaffold\n");
68+
69+
await patchFiles(createInitData(dir, false));
70+
71+
const packageJson = JSON.parse(
72+
await readFile(join(dir, "package.json"), "utf8"),
73+
) as { type?: string };
74+
assert.equal(packageJson.type, "module");
75+
});
76+
});
77+
6478
function createInitData(
6579
dir: string,
6680
allowNonEmpty: boolean,

‎packages/init/src/action/patch.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ const parseJsoncObject = (
327327
): Record<PropertyKey, unknown> => {
328328
const errors: ParseError[] = [];
329329
const value = parseJsonc(jsonString, errors, { allowTrailingComma: true });
330+
if (value === undefined && isEmptyJsonc(errors)) return {};
330331
if (errors.length > 0) {
331332
throw new SyntaxError(
332333
errors
@@ -342,6 +343,10 @@ const parseJsoncObject = (
342343
return value as Record<PropertyKey, unknown>;
343344
};
344345

346+
const isEmptyJsonc = (errors: readonly ParseError[]): boolean =>
347+
errors.length > 0 &&
348+
errors.every(({ error }) => printParseErrorCode(error) === "ValueExpected");
349+
345350
/**
346351
* Appends new text content to existing text content line by line.
347352
* Concatenates new content lines with existing content lines,

‎packages/init/src/test/create.ts‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import $ from "@david/dax";
22
import { filter, isEmpty, pipe, toArray } from "@fxts/core";
33
import { values } from "@optique/core";
4-
import { appendFile, mkdir } from "node:fs/promises";
4+
import { appendFile, mkdir, stat } from "node:fs/promises";
55
import { join, sep } from "node:path";
66
import process from "node:process";
77
import packageManagers from "../json/pm.json" with { type: "json" };
@@ -130,6 +130,7 @@ async function validateDevToolScripts(
130130
MessageQueue,
131131
];
132132
if (packageManager === "deno") return true;
133+
if (!(await hasInstalledNodeDependencies(dir))) return true;
133134

134135
for (const script of ["format:check", "lint"]) {
135136
const result = await $`${[packageManager, "run", script]}`
@@ -145,6 +146,14 @@ async function validateDevToolScripts(
145146
return true;
146147
}
147148

149+
async function hasInstalledNodeDependencies(dir: string): Promise<boolean> {
150+
try {
151+
return (await stat(join(dir, "node_modules"))).isDirectory();
152+
} catch {
153+
return false;
154+
}
155+
}
156+
148157
export function filterOptions(
149158
options: GeneratedType<ReturnType<typeof generateTestCases>>,
150159
): boolean {

0 commit comments

Comments
 (0)