mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 06:57:10 +00:00
Simplify validation scripts
This commit is contained in:
parent
880ef3f35f
commit
b14969cbfb
6 changed files with 93 additions and 57 deletions
|
|
@ -11,13 +11,13 @@ npm run check
|
|||
npm run build
|
||||
```
|
||||
|
||||
Run `npm run format` after edits and before `npm run check` so Prettier-only issues are fixed upfront. `npm run check` runs TypeScript type checking, unit tests, lint checks, Prettier check, CSS build verification, and a production esbuild bundle. The local `npm run check` path runs checks in parallel and uses local caches where available. Use `npm run check:ci` when you need to reproduce the sequential, non-cached CI validation path.
|
||||
Run `npm run format` after edits and before `npm run check` so Prettier-only issues are fixed upfront. `npm run check` runs TypeScript type checking, unit tests, lint checks, and Prettier check in parallel, then runs the CSS build verification and production esbuild bundle as the final step. `npm run check` and `npm run check:ci` use the same script runner; the local path uses caches where available, while `npm run check:ci` uses the non-cached validation commands used by CI.
|
||||
|
||||
## Generated and Loaded Files
|
||||
|
||||
`main.js`, `styles.css`, `data.json`, and `node_modules/` are ignored by Git. `main.js` and `styles.css` are still the files Obsidian loads, so run `npm run build` after source changes.
|
||||
|
||||
CSS is authored in `src/styles/` and generated into the ignored root `styles.css` release asset. Use `npm run build:styles` when only regenerating CSS, or `npm run build:styles:check` to verify that the authored CSS can be rendered.
|
||||
CSS is authored in `src/styles/` and generated into the ignored root `styles.css` release asset. Use `npm run build:styles` when only regenerating CSS; it also verifies the authored CSS order before writing `styles.css`.
|
||||
|
||||
The app-server TypeScript bindings in `src/generated/app-server/` are generated from the installed Codex CLI:
|
||||
|
||||
|
|
@ -72,9 +72,8 @@ Use the local API baseline report when checking whether the development environm
|
|||
|
||||
```sh
|
||||
npm run api:baseline
|
||||
npm run api:baseline:check
|
||||
```
|
||||
|
||||
Obsidian runtime compatibility is declared through `manifest.json` and `versions.json`. The `obsidian` npm package provides compile-time TypeScript API definitions; it is not runtime validation for an Obsidian app-version matrix. Because the project does not run app-version smoke tests, keep the API type package in the same minor as `manifest.minAppVersion` and use the latest patch in that minor for local type checking. Raise `manifest.minAppVersion` only when intentionally adopting a newer Obsidian app/API minor.
|
||||
Obsidian runtime compatibility is declared through `manifest.json` and `versions.json`. The `obsidian` npm package provides compile-time TypeScript API definitions; it is not runtime validation for an Obsidian app-version matrix. Because the project does not run app-version smoke tests, keep the API type package in the same minor as `manifest.minAppVersion` and use the latest patch in that minor for local type checking. `npm run api:baseline` exits non-zero when the local environment or recorded baselines drift. Raise `manifest.minAppVersion` only when intentionally adopting a newer Obsidian app/API minor.
|
||||
|
||||
Codex app-server compatibility is managed by Codex CLI minor version. README records the tested Codex CLI patch version, and the baseline check verifies that the local `codex --version` is in the same minor before app-server binding or compatibility work.
|
||||
|
|
|
|||
16
package.json
16
package.json
|
|
@ -15,24 +15,16 @@
|
|||
"homepage": "https://github.com/murashit/codex-panel#readme",
|
||||
"scripts": {
|
||||
"api:baseline": "node scripts/api-baseline.mjs",
|
||||
"api:baseline:check": "node scripts/api-baseline.mjs --check",
|
||||
"build": "node esbuild.config.mjs",
|
||||
"build:styles": "node scripts/build-styles.mjs",
|
||||
"build:styles:check": "node scripts/build-styles.mjs --check",
|
||||
"check": "concurrently --group --pad-prefix --names typecheck,test,lint:ts,lint:css,lint:css-usage,lint:deps,lint:unused,format:check,build:styles:check,build \"npm run --silent typecheck\" \"npm run --silent test\" \"npm run --silent lint:ts\" \"npm run --silent lint:css\" \"npm run --silent lint:css-usage\" \"npm run --silent lint:deps\" \"npm run --silent lint:unused\" \"npm run --silent format:check\" \"npm run --silent build:styles:check\" \"npm run --silent build\"",
|
||||
"check:ci": "npm run typecheck:ci && npm run test:ci && npm run lint:ci && npm run format:check:ci && npm run build:styles:check && npm run build",
|
||||
"check": "node scripts/check.mjs",
|
||||
"check:ci": "node scripts/check.mjs --ci",
|
||||
"format": "node scripts/format.mjs",
|
||||
"format:check": "node scripts/format.mjs --check --cache",
|
||||
"format:check:ci": "node scripts/format.mjs --check",
|
||||
"generate:app-server-types": "node scripts/generate-app-server-types.mjs",
|
||||
"lint": "concurrently --group --pad-prefix --names lint:ts,lint:css,lint:css-usage,lint:deps,lint:unused \"npm run --silent lint:ts\" \"npm run --silent lint:css\" \"npm run --silent lint:css-usage\" \"npm run --silent lint:deps\" \"npm run --silent lint:unused\"",
|
||||
"lint:ci": "npm run lint:ts:ci && npm run lint:css && npm run lint:css-usage && npm run lint:deps && npm run lint:unused",
|
||||
"lint:css": "stylelint \"src/**/*.css\" --max-warnings=0",
|
||||
"lint:css-usage": "node scripts/lint/check-css-usage.mjs --fail-on-candidates",
|
||||
"lint:deps": "node scripts/lint/check-import-cycles.mjs",
|
||||
"lint:ts": "eslint src tests scripts \"*.config.ts\" \"*.config.mjs\" --max-warnings=0 --cache --cache-strategy content --cache-location node_modules/.cache/eslint/.eslintcache",
|
||||
"lint:ts:ci": "eslint src tests scripts \"*.config.ts\" \"*.config.mjs\" --max-warnings=0",
|
||||
"lint:unused": "knip --no-progress",
|
||||
"lint": "node scripts/check.mjs --lint",
|
||||
"lint:ci": "node scripts/check.mjs --ci --lint",
|
||||
"release:check": "node scripts/release/check.mjs",
|
||||
"release:preflight": "node scripts/release/preflight.mjs",
|
||||
"release:prepare": "node scripts/release/prepare.mjs",
|
||||
|
|
|
|||
|
|
@ -3,17 +3,20 @@ import { spawnSync } from "node:child_process";
|
|||
import { readJson } from "./utils.mjs";
|
||||
|
||||
const args = new Set(process.argv.slice(2));
|
||||
const shouldCheck = args.has("--check");
|
||||
const asJson = args.has("--json");
|
||||
const validArgs = new Set(["--json"]);
|
||||
|
||||
for (const arg of args) {
|
||||
if (!validArgs.has(arg)) {
|
||||
console.error("Usage: node scripts/api-baseline.mjs [--json]");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function fail(message) {
|
||||
failures.push(message);
|
||||
}
|
||||
|
||||
function warn(message) {
|
||||
warnings.push(message);
|
||||
}
|
||||
|
||||
function parseSemver(value) {
|
||||
const match = String(value ?? "").match(/\b(\d+)\.(\d+)\.(\d+)\b/);
|
||||
if (!match) return null;
|
||||
|
|
@ -111,8 +114,6 @@ function displayValue(value) {
|
|||
}
|
||||
|
||||
const failures = [];
|
||||
const warnings = [];
|
||||
|
||||
const packageJson = await readJson("package.json");
|
||||
const packageLockJson = await readJson("package-lock.json");
|
||||
const manifestJson = await readJson("manifest.json");
|
||||
|
|
@ -148,9 +149,7 @@ if (!codexReadmeSemver) {
|
|||
fail("README.md Compatibility table must define `codex.testedCliVersion` as X.Y.Z.");
|
||||
}
|
||||
if (!codexLocalSemver) {
|
||||
const message = "local codex --version could not be read.";
|
||||
if (shouldCheck) fail(message);
|
||||
else warn(message);
|
||||
fail("local codex --version could not be read.");
|
||||
}
|
||||
if (codexReadmeSemver && codexLocalSemver && minorKey(codexReadmeSemver) !== minorKey(codexLocalSemver)) {
|
||||
fail(`local Codex CLI minor ${minorKey(codexLocalSemver)} does not match compatibility table minor ${minorKey(codexReadmeSemver)}.`);
|
||||
|
|
@ -215,7 +214,6 @@ const report = {
|
|||
lockedPackageVersion: obsidianLockVersion,
|
||||
lockedPackageMinor: minorKey(obsidianLockSemver),
|
||||
},
|
||||
warnings,
|
||||
failures,
|
||||
};
|
||||
|
||||
|
|
@ -243,11 +241,6 @@ if (asJson) {
|
|||
console.log(` package obsidian: ${displayValue(report.obsidian.packageDependency)}`);
|
||||
console.log(` package range: ${report.obsidian.packageDependencyRange}`);
|
||||
console.log(` package-lock obsidian: ${displayValue(report.obsidian.lockedPackageVersion)}`);
|
||||
if (warnings.length > 0) {
|
||||
console.log("");
|
||||
console.log("Warnings");
|
||||
for (const message of warnings) console.log(` - ${message}`);
|
||||
}
|
||||
if (failures.length > 0) {
|
||||
console.log("");
|
||||
console.log("Failures");
|
||||
|
|
@ -255,4 +248,4 @@ if (asJson) {
|
|||
}
|
||||
}
|
||||
|
||||
if (shouldCheck && failures.length > 0) process.exit(1);
|
||||
if (failures.length > 0) process.exit(1);
|
||||
|
|
|
|||
|
|
@ -5,34 +5,21 @@ import { fileURLToPath } from "node:url";
|
|||
const sourceDir = path.join("src", "styles");
|
||||
const orderPath = path.join(sourceDir, "order.json");
|
||||
const outputPath = "styles.css";
|
||||
const checkMode = process.argv.includes("--check");
|
||||
const validArgs = new Set(["--check"]);
|
||||
|
||||
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
||||
for (const arg of process.argv.slice(2)) {
|
||||
if (!validArgs.has(arg)) {
|
||||
console.error("Usage: node scripts/build-styles.mjs [--check]");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (checkMode) {
|
||||
await checkStyles();
|
||||
} else {
|
||||
await buildStyles();
|
||||
if (process.argv.length > 2) {
|
||||
console.error("Usage: node scripts/build-styles.mjs");
|
||||
process.exit(1);
|
||||
}
|
||||
await buildStyles();
|
||||
}
|
||||
|
||||
export async function buildStyles() {
|
||||
await checkStyleOrder();
|
||||
await mkdir(path.dirname(outputPath), { recursive: true });
|
||||
await writeFile(outputPath, await renderStyles());
|
||||
}
|
||||
|
||||
async function checkStyles() {
|
||||
await checkStyleOrder();
|
||||
await renderStyles();
|
||||
}
|
||||
|
||||
export async function renderStyles() {
|
||||
const parts = [];
|
||||
|
||||
|
|
|
|||
60
scripts/check.mjs
Normal file
60
scripts/check.mjs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import concurrently from "concurrently";
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const validArgs = new Set(["--ci", "--lint"]);
|
||||
const unknownArgs = args.filter((arg) => !validArgs.has(arg));
|
||||
|
||||
if (unknownArgs.length > 0) {
|
||||
console.error("Usage: node scripts/check.mjs [--ci] [--lint]");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const ciMode = args.includes("--ci");
|
||||
const lintOnly = args.includes("--lint");
|
||||
const concurrentOptions = {
|
||||
group: true,
|
||||
padPrefix: true,
|
||||
prefix: "name",
|
||||
};
|
||||
|
||||
try {
|
||||
if (lintOnly) {
|
||||
await runConcurrently(lintCommands({ ciMode }));
|
||||
} else {
|
||||
await runConcurrently(checkCommands({ ciMode }));
|
||||
await runConcurrently([{ name: "build", command: "node esbuild.config.mjs" }]);
|
||||
}
|
||||
} catch {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
async function runConcurrently(commands) {
|
||||
await concurrently(commands, concurrentOptions).result;
|
||||
}
|
||||
|
||||
function checkCommands({ ciMode }) {
|
||||
const suffix = ciMode ? ":ci" : "";
|
||||
const formatArgs = ciMode ? "--check" : "--check --cache";
|
||||
|
||||
return [
|
||||
{ name: "typecheck", command: `npm run --silent typecheck${suffix}` },
|
||||
{ name: "test", command: `npm run --silent test${suffix}` },
|
||||
...lintCommands({ ciMode, namePrefix: "lint:" }),
|
||||
{ name: "format:check", command: `node scripts/format.mjs ${formatArgs}` },
|
||||
];
|
||||
}
|
||||
|
||||
function lintCommands({ ciMode, namePrefix = "" }) {
|
||||
const eslintCacheArgs = ciMode ? "" : " --cache --cache-strategy content --cache-location node_modules/.cache/eslint/.eslintcache";
|
||||
|
||||
return [
|
||||
{
|
||||
name: `${namePrefix}ts`,
|
||||
command: `eslint src tests scripts "*.config.ts" "*.config.mjs" --max-warnings=0${eslintCacheArgs}`,
|
||||
},
|
||||
{ name: `${namePrefix}css`, command: 'stylelint "src/**/*.css" --max-warnings=0' },
|
||||
{ name: `${namePrefix}css-usage`, command: "node scripts/lint/check-css-usage.mjs --fail-on-candidates" },
|
||||
{ name: `${namePrefix}deps`, command: "node scripts/lint/check-import-cycles.mjs" },
|
||||
{ name: `${namePrefix}unused`, command: "knip --no-progress" },
|
||||
];
|
||||
}
|
||||
|
|
@ -8,13 +8,9 @@ const repoRoot = process.cwd();
|
|||
|
||||
describe("development scripts", () => {
|
||||
it("fails style builds when CSS files are missing from the style order file", async () => {
|
||||
const cwd = await tempWorkspace();
|
||||
await mkdir(path.join(cwd, "src", "styles"), { recursive: true });
|
||||
await writeJson(path.join(cwd, "src", "styles", "order.json"), ["00-tokens.css"]);
|
||||
await writeFile(path.join(cwd, "src", "styles", "00-tokens.css"), ".codex-panel { color: var(--text-normal); }\n");
|
||||
await writeFile(path.join(cwd, "src", "styles", "10-unlisted.css"), ".codex-panel__extra { display: block; }\n");
|
||||
const cwd = await styleOrderFixture();
|
||||
|
||||
const result = runNodeScript("scripts/build-styles.mjs", ["--check"], cwd);
|
||||
const result = runNodeScript("scripts/build-styles.mjs", [], cwd);
|
||||
|
||||
expect(result.status).toBe(1);
|
||||
expect(result.stderr).toContain("CSS files missing from src/styles/order.json: 10-unlisted.css");
|
||||
|
|
@ -209,6 +205,15 @@ async function tempWorkspace(): Promise<string> {
|
|||
return mkdtemp(path.join(tmpdir(), "codex-panel-scripts-"));
|
||||
}
|
||||
|
||||
async function styleOrderFixture(): Promise<string> {
|
||||
const cwd = await tempWorkspace();
|
||||
await mkdir(path.join(cwd, "src", "styles"), { recursive: true });
|
||||
await writeJson(path.join(cwd, "src", "styles", "order.json"), ["00-tokens.css"]);
|
||||
await writeFile(path.join(cwd, "src", "styles", "00-tokens.css"), ".codex-panel { color: var(--text-normal); }\n");
|
||||
await writeFile(path.join(cwd, "src", "styles", "10-unlisted.css"), ".codex-panel__extra { display: block; }\n");
|
||||
return cwd;
|
||||
}
|
||||
|
||||
async function writeImportCycleFixture(cwd: string, files: Record<string, string>): Promise<void> {
|
||||
await writeJson(path.join(cwd, "tsconfig.json"), {
|
||||
compilerOptions: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue