2026-07-21 19:08:20 +00:00
|
|
|
const fs = require("fs");
|
|
|
|
|
const path = require("path");
|
2026-07-21 15:06:22 +00:00
|
|
|
|
2026-07-21 19:08:20 +00:00
|
|
|
const rootDir = path.resolve(__dirname, "..");
|
2026-07-21 15:06:22 +00:00
|
|
|
|
|
|
|
|
function exitWithError(msg) {
|
|
|
|
|
console.error(`❌ VALIDATION FAILED: ${msg}`);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-21 19:08:20 +00:00
|
|
|
console.log("🔍 Running regression validation checks...");
|
2026-07-21 15:06:22 +00:00
|
|
|
|
|
|
|
|
// 1. Check root manifest.json and versions.json existence
|
2026-07-21 19:08:20 +00:00
|
|
|
const rootManifestPath = path.join(rootDir, "manifest.json");
|
|
|
|
|
const rootVersionsPath = path.join(rootDir, "versions.json");
|
2026-07-21 15:06:22 +00:00
|
|
|
|
|
|
|
|
if (!fs.existsSync(rootManifestPath)) {
|
2026-07-21 19:08:20 +00:00
|
|
|
exitWithError("root manifest.json is missing!");
|
2026-07-21 15:06:22 +00:00
|
|
|
}
|
|
|
|
|
if (!fs.existsSync(rootVersionsPath)) {
|
2026-07-21 19:08:20 +00:00
|
|
|
exitWithError("root versions.json is missing!");
|
2026-07-21 15:06:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let manifest;
|
|
|
|
|
try {
|
2026-07-21 19:08:20 +00:00
|
|
|
manifest = JSON.parse(fs.readFileSync(rootManifestPath, "utf8"));
|
2026-07-21 15:06:22 +00:00
|
|
|
} catch (e) {
|
|
|
|
|
exitWithError(`Failed to parse root manifest.json: ${e.message}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let versions;
|
|
|
|
|
try {
|
2026-07-21 19:08:20 +00:00
|
|
|
versions = JSON.parse(fs.readFileSync(rootVersionsPath, "utf8"));
|
2026-07-21 15:06:22 +00:00
|
|
|
} catch (e) {
|
|
|
|
|
exitWithError(`Failed to parse root versions.json: ${e.message}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure Obsidian plugin details
|
2026-07-21 19:45:33 +00:00
|
|
|
if (manifest.id !== "md-discord-syntax") {
|
2026-07-21 19:08:20 +00:00
|
|
|
exitWithError(
|
2026-07-21 19:45:33 +00:00
|
|
|
`manifest.json id must be "md-discord-syntax", got "${manifest.id}"`,
|
2026-07-21 19:08:20 +00:00
|
|
|
);
|
2026-07-21 15:06:22 +00:00
|
|
|
}
|
2026-07-21 19:08:20 +00:00
|
|
|
if (manifest.name !== "Discord Syntax") {
|
|
|
|
|
exitWithError(
|
|
|
|
|
`manifest.json name must be "Discord Syntax", got "${manifest.name}"`,
|
|
|
|
|
);
|
2026-07-21 15:06:22 +00:00
|
|
|
}
|
2026-07-21 19:08:20 +00:00
|
|
|
if (manifest.author !== "Edems-DEV") {
|
|
|
|
|
exitWithError(
|
|
|
|
|
`manifest.json author must be "Edems-DEV", got "${manifest.author}"`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (
|
|
|
|
|
typeof manifest.description !== "string" ||
|
|
|
|
|
/\bobsidian\b/i.test(manifest.description) ||
|
|
|
|
|
!/[.!?]$/.test(manifest.description)
|
|
|
|
|
) {
|
|
|
|
|
exitWithError(
|
|
|
|
|
'manifest.json description must omit "Obsidian" and end with punctuation',
|
|
|
|
|
);
|
2026-07-21 15:06:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. Synchronize root manifest.json and versions.json to packages/obsidian/
|
2026-07-21 19:08:20 +00:00
|
|
|
const nestedManifestPath = path.join(
|
|
|
|
|
rootDir,
|
|
|
|
|
"packages",
|
|
|
|
|
"obsidian",
|
|
|
|
|
"manifest.json",
|
|
|
|
|
);
|
|
|
|
|
const nestedVersionsPath = path.join(
|
|
|
|
|
rootDir,
|
|
|
|
|
"packages",
|
|
|
|
|
"obsidian",
|
|
|
|
|
"versions.json",
|
|
|
|
|
);
|
2026-07-21 15:06:22 +00:00
|
|
|
|
|
|
|
|
try {
|
2026-07-21 19:08:20 +00:00
|
|
|
fs.writeFileSync(
|
|
|
|
|
nestedManifestPath,
|
|
|
|
|
JSON.stringify(manifest, null, 2) + "\n",
|
|
|
|
|
);
|
|
|
|
|
console.log(
|
|
|
|
|
"✅ Synchronized root manifest.json to packages/obsidian/manifest.json",
|
|
|
|
|
);
|
|
|
|
|
fs.writeFileSync(
|
|
|
|
|
nestedVersionsPath,
|
|
|
|
|
JSON.stringify(versions, null, 2) + "\n",
|
|
|
|
|
);
|
|
|
|
|
console.log(
|
|
|
|
|
"✅ Synchronized root versions.json to packages/obsidian/versions.json",
|
|
|
|
|
);
|
2026-07-21 15:06:22 +00:00
|
|
|
} catch (e) {
|
|
|
|
|
exitWithError(`Failed to synchronize manifest/versions files: ${e.message}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. Validate packages/obsidian/package.json version matches manifest
|
2026-07-21 19:08:20 +00:00
|
|
|
const obsidianPkgPath = path.join(
|
|
|
|
|
rootDir,
|
|
|
|
|
"packages",
|
|
|
|
|
"obsidian",
|
|
|
|
|
"package.json",
|
|
|
|
|
);
|
2026-07-21 15:06:22 +00:00
|
|
|
let obsidianPkg;
|
|
|
|
|
try {
|
2026-07-21 19:08:20 +00:00
|
|
|
obsidianPkg = JSON.parse(fs.readFileSync(obsidianPkgPath, "utf8"));
|
2026-07-21 15:06:22 +00:00
|
|
|
} catch (e) {
|
2026-07-21 19:08:20 +00:00
|
|
|
exitWithError(
|
|
|
|
|
`Failed to read/parse packages/obsidian/package.json: ${e.message}`,
|
|
|
|
|
);
|
2026-07-21 15:06:22 +00:00
|
|
|
}
|
|
|
|
|
|
2026-07-21 19:45:33 +00:00
|
|
|
if (obsidianPkg.name !== "md-discord-syntax") {
|
2026-07-21 19:08:20 +00:00
|
|
|
exitWithError(
|
2026-07-21 19:45:33 +00:00
|
|
|
`packages/obsidian/package.json name must be "md-discord-syntax", got "${obsidianPkg.name}"`,
|
2026-07-21 19:08:20 +00:00
|
|
|
);
|
2026-07-21 15:45:00 +00:00
|
|
|
}
|
|
|
|
|
|
2026-07-21 15:06:22 +00:00
|
|
|
if (obsidianPkg.version !== manifest.version) {
|
2026-07-21 19:08:20 +00:00
|
|
|
exitWithError(
|
|
|
|
|
`packages/obsidian/package.json version (${obsidianPkg.version}) does not match manifest.json version (${manifest.version})`,
|
|
|
|
|
);
|
2026-07-21 15:06:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. Verify packages/obsidian/src/styles.css exists
|
2026-07-21 19:08:20 +00:00
|
|
|
const stylesSrcPath = path.join(
|
|
|
|
|
rootDir,
|
|
|
|
|
"packages",
|
|
|
|
|
"obsidian",
|
|
|
|
|
"src",
|
|
|
|
|
"styles.css",
|
|
|
|
|
);
|
2026-07-21 15:06:22 +00:00
|
|
|
if (!fs.existsSync(stylesSrcPath)) {
|
|
|
|
|
exitWithError(`packages/obsidian/src/styles.css is missing!`);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-21 15:28:51 +00:00
|
|
|
// 5. Check package metadata dependencies & publish tarball configuration
|
2026-07-21 19:08:20 +00:00
|
|
|
const corePkgPath = path.join(rootDir, "packages", "core", "package.json");
|
2026-07-21 15:28:51 +00:00
|
|
|
let corePkg;
|
|
|
|
|
try {
|
2026-07-21 19:08:20 +00:00
|
|
|
corePkg = JSON.parse(fs.readFileSync(corePkgPath, "utf8"));
|
2026-07-21 15:28:51 +00:00
|
|
|
} catch (e) {
|
2026-07-21 19:08:20 +00:00
|
|
|
exitWithError(
|
|
|
|
|
`Failed to read/parse packages/core/package.json: ${e.message}`,
|
|
|
|
|
);
|
2026-07-21 15:28:51 +00:00
|
|
|
}
|
|
|
|
|
|
2026-07-21 19:08:20 +00:00
|
|
|
if (corePkg.name !== "@edems-dev/md-discord-syntax-core") {
|
|
|
|
|
exitWithError(
|
|
|
|
|
`packages/core/package.json name must be "@edems-dev/md-discord-syntax-core", got "${corePkg.name}"`,
|
|
|
|
|
);
|
2026-07-21 15:28:51 +00:00
|
|
|
}
|
|
|
|
|
|
2026-07-21 19:08:20 +00:00
|
|
|
const remarkPkgPath = path.join(rootDir, "packages", "remark", "package.json");
|
2026-07-21 15:06:22 +00:00
|
|
|
let remarkPkg;
|
|
|
|
|
try {
|
2026-07-21 19:08:20 +00:00
|
|
|
remarkPkg = JSON.parse(fs.readFileSync(remarkPkgPath, "utf8"));
|
2026-07-21 15:06:22 +00:00
|
|
|
} catch (e) {
|
2026-07-21 19:08:20 +00:00
|
|
|
exitWithError(
|
|
|
|
|
`Failed to read/parse packages/remark/package.json: ${e.message}`,
|
|
|
|
|
);
|
2026-07-21 15:06:22 +00:00
|
|
|
}
|
|
|
|
|
|
2026-07-21 19:08:20 +00:00
|
|
|
if (remarkPkg.name !== "@edems-dev/remark-discord-syntax") {
|
|
|
|
|
exitWithError(
|
|
|
|
|
`packages/remark/package.json name must be "@edems-dev/remark-discord-syntax", got "${remarkPkg.name}"`,
|
|
|
|
|
);
|
2026-07-21 15:45:00 +00:00
|
|
|
}
|
|
|
|
|
|
2026-07-21 19:08:20 +00:00
|
|
|
if (remarkPkg.dependencies?.["@md-discord-syntax/core"]) {
|
|
|
|
|
exitWithError(
|
|
|
|
|
`@edems-dev/remark-discord-syntax still has reference to old scope "@md-discord-syntax/core"`,
|
|
|
|
|
);
|
2026-07-21 15:28:51 +00:00
|
|
|
}
|
|
|
|
|
|
2026-07-21 19:08:20 +00:00
|
|
|
const remarkCoreDep =
|
|
|
|
|
remarkPkg.dependencies?.["@edems-dev/md-discord-syntax-core"];
|
2026-07-21 15:06:22 +00:00
|
|
|
if (!remarkCoreDep) {
|
2026-07-21 19:08:20 +00:00
|
|
|
exitWithError(
|
|
|
|
|
`@edems-dev/remark-discord-syntax is missing dependency on "@edems-dev/md-discord-syntax-core"`,
|
|
|
|
|
);
|
2026-07-21 15:06:22 +00:00
|
|
|
}
|
2026-07-21 19:08:20 +00:00
|
|
|
if (remarkCoreDep.startsWith("file:")) {
|
|
|
|
|
exitWithError(
|
|
|
|
|
`@edems-dev/remark-discord-syntax depends on local file spec for "@edems-dev/md-discord-syntax-core" ("${remarkCoreDep}"). Must use normal semver.`,
|
|
|
|
|
);
|
2026-07-21 15:28:51 +00:00
|
|
|
}
|
|
|
|
|
|
2026-07-21 19:08:20 +00:00
|
|
|
if (obsidianPkg.dependencies?.["@md-discord-syntax/core"]) {
|
|
|
|
|
exitWithError(
|
|
|
|
|
`discord-syntax still has reference to old scope "@md-discord-syntax/core"`,
|
|
|
|
|
);
|
2026-07-21 15:06:22 +00:00
|
|
|
}
|
|
|
|
|
|
2026-07-21 19:08:20 +00:00
|
|
|
const obsidianCoreDep =
|
|
|
|
|
obsidianPkg.dependencies?.["@edems-dev/md-discord-syntax-core"];
|
|
|
|
|
if (obsidianCoreDep && obsidianCoreDep.startsWith("file:")) {
|
|
|
|
|
exitWithError(
|
|
|
|
|
`discord-syntax depends on local file spec for "@edems-dev/md-discord-syntax-core" ("${obsidianCoreDep}"). Must use normal semver.`,
|
|
|
|
|
);
|
2026-07-21 15:28:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure published npm packages (core & remark) configure tarballs & safe prepack
|
2026-07-21 19:08:20 +00:00
|
|
|
for (const pkgName of ["core", "remark"]) {
|
|
|
|
|
const pkgJsonPath = path.join(rootDir, "packages", pkgName, "package.json");
|
|
|
|
|
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8"));
|
2026-07-21 15:28:51 +00:00
|
|
|
|
2026-07-21 19:08:20 +00:00
|
|
|
if (!Array.isArray(pkg.files) || !pkg.files.includes("dist")) {
|
|
|
|
|
exitWithError(
|
|
|
|
|
`packages/${pkgName}/package.json "files" array must include "dist"`,
|
|
|
|
|
);
|
2026-07-21 15:28:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!pkg.scripts?.prepack) {
|
2026-07-21 19:08:20 +00:00
|
|
|
exitWithError(
|
|
|
|
|
`packages/${pkgName}/package.json is missing safe "prepack" script`,
|
|
|
|
|
);
|
2026-07-21 15:28:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure entrypoints exist in dist
|
2026-07-21 19:08:20 +00:00
|
|
|
const distJsPath = path.join(
|
|
|
|
|
rootDir,
|
|
|
|
|
"packages",
|
|
|
|
|
pkgName,
|
|
|
|
|
"dist",
|
|
|
|
|
"index.js",
|
|
|
|
|
);
|
|
|
|
|
const distDtsPath = path.join(
|
|
|
|
|
rootDir,
|
|
|
|
|
"packages",
|
|
|
|
|
pkgName,
|
|
|
|
|
"dist",
|
|
|
|
|
"index.d.ts",
|
|
|
|
|
);
|
2026-07-21 15:28:51 +00:00
|
|
|
if (!fs.existsSync(distJsPath)) {
|
2026-07-21 19:08:20 +00:00
|
|
|
exitWithError(
|
|
|
|
|
`packages/${pkgName}/dist/index.js is missing! Make sure to run build first.`,
|
|
|
|
|
);
|
2026-07-21 15:28:51 +00:00
|
|
|
}
|
|
|
|
|
if (!fs.existsSync(distDtsPath)) {
|
2026-07-21 19:08:20 +00:00
|
|
|
exitWithError(
|
|
|
|
|
`packages/${pkgName}/dist/index.d.ts is missing! Make sure to run build first.`,
|
|
|
|
|
);
|
2026-07-21 15:28:51 +00:00
|
|
|
}
|
2026-07-21 15:06:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 6. Verify release assets existence and non-emptiness
|
|
|
|
|
const releaseAssets = [
|
2026-07-21 19:08:20 +00:00
|
|
|
path.join(rootDir, "packages", "obsidian", "main.js"),
|
|
|
|
|
path.join(rootDir, "packages", "obsidian", "manifest.json"),
|
|
|
|
|
path.join(rootDir, "packages", "obsidian", "styles.css"),
|
2026-07-21 15:06:22 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
for (const asset of releaseAssets) {
|
|
|
|
|
if (!fs.existsSync(asset)) {
|
2026-07-21 19:08:20 +00:00
|
|
|
exitWithError(
|
|
|
|
|
`Missing required release asset: ${path.relative(rootDir, asset)}. Make sure to run the build first!`,
|
|
|
|
|
);
|
2026-07-21 15:06:22 +00:00
|
|
|
}
|
|
|
|
|
const stats = fs.statSync(asset);
|
|
|
|
|
if (stats.size === 0) {
|
|
|
|
|
exitWithError(`Release asset ${path.relative(rootDir, asset)} is empty!`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-21 19:08:20 +00:00
|
|
|
console.log("✨ All validation checks passed successfully!");
|