Skip to content
Merged
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
22 changes: 20 additions & 2 deletions packages/ztd-cli/tests/publishWorkflow.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ test('published-package mode includes the rawsql-ts getting-started smoke path',
expect(publishedPackageModeScript).toContain('const formatter = new SqlFormatter();');
});

test('published-package mode only runs the rawsql-ts getting-started smoke when its tarball is present', () => {
const coreSection = publishedPackageModeScript.slice(
publishedPackageModeScript.indexOf('function verifyCoreGettingStarted(packages) {'),
publishedPackageModeScript.indexOf('function verifyNpmPrimaryPath(packages) {'),
);

expect(coreSection).toContain('const tarballDependencies = createTarballDependencyMap(packages);');
expect(coreSection).toContain('if (!hasTarballDependency(tarballDependencies, "rawsql-ts")) {');
expect(coreSection).toContain('return null;');
expect(coreSection).toContain('"rawsql-ts": tarballDependencies["rawsql-ts"],');
});

test('published-package mode expects local-source guard scripts from npm consumer scaffolds', () => {
const npmSection = publishedPackageModeScript.slice(
publishedPackageModeScript.indexOf('function verifyNpmPrimaryPath(packages) {'),
Expand Down Expand Up @@ -131,9 +143,15 @@ test('packed tarball install smoke only runs commands for tarballs included in t
});

test('published-package smoke rebinds scaffold runtime dependencies to packed tarballs', () => {
expect(publishedPackageModeScript).toContain('for (const sectionName of ["dependencies", "optionalDependencies", "peerDependencies"])');
expect(publishedPackageModeScript).toContain('function createPublishedDependencyRangeMap(packages) {');
expect(publishedPackageModeScript).toContain('dependencyName === "rawsql-ts" || dependencyName.startsWith("@rawsql-ts/")');
expect(publishedPackageModeScript).toContain('for (const sectionName of ["dependencies", "optionalDependencies", "peerDependencies", "devDependencies"])');
expect(publishedPackageModeScript).toContain('section[dependencyName] = tarballDependencies[dependencyName];');
expect(publishedPackageModeScript).toContain('restoreTarballDependencies(appDir, tarballDependencies);');
expect(publishedPackageModeScript).toContain('currentRange.startsWith("file:")');
expect(publishedPackageModeScript).toContain('section[dependencyName] = publishedDependencyRanges.get(dependencyName);');
expect(publishedPackageModeScript).toContain('fs.rmSync(path.join(directory, "package-lock.json"), { force: true });');
expect(publishedPackageModeScript).toContain('fs.rmSync(path.join(directory, "node_modules"), { force: true, recursive: true });');
expect(publishedPackageModeScript).toContain('restorePublishedDependencyRanges(appDir, packages);');
});

test('publish plan can include unpublished workspace dependencies required by published scaffolds', () => {
Expand Down
70 changes: 60 additions & 10 deletions scripts/verify-published-package-mode.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,30 @@ function hasTarballDependency(tarballDependencies, packageName) {
return typeof tarballDependencies[packageName] === "string";
}

function createPublishedDependencyRangeMap(packages) {
const dependencyRanges = new Map();

for (const pkg of packages) {
for (const sectionName of ["dependencies", "optionalDependencies", "peerDependencies", "devDependencies"]) {
const section = pkg.manifest[sectionName];
if (!section || typeof section !== "object" || Array.isArray(section)) {
continue;
}

for (const [dependencyName, dependencyRange] of Object.entries(section)) {
if (
typeof dependencyRange === "string"
&& (dependencyName === "rawsql-ts" || dependencyName.startsWith("@rawsql-ts/"))
) {
dependencyRanges.set(dependencyName, dependencyRange);
}
}
}
}

return dependencyRanges;
}

function setPackageTypeModule(directory) {
const packageJsonPath = path.join(directory, "package.json");
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
Expand All @@ -248,25 +272,45 @@ function readPackageJson(directory) {
return JSON.parse(fs.readFileSync(path.join(directory, "package.json"), "utf8"));
}

function restoreTarballDependencies(directory, tarballDependencies) {
function restorePublishedDependencyRanges(directory, packages) {
const packageJsonPath = path.join(directory, "package.json");
const packageJson = readPackageJson(directory);
for (const sectionName of ["dependencies", "optionalDependencies", "peerDependencies"]) {
const tarballDependencies = createTarballDependencyMap(packages);
const publishedDependencyRanges = createPublishedDependencyRangeMap(packages);
let changed = false;

for (const sectionName of ["dependencies", "optionalDependencies", "peerDependencies", "devDependencies"]) {
const section = packageJson[sectionName];
if (!section || typeof section !== "object" || Array.isArray(section)) {
continue;
}
for (const dependencyName of Object.keys(section)) {

for (const [dependencyName, currentRange] of Object.entries(section)) {
if (typeof tarballDependencies[dependencyName] === "string") {
section[dependencyName] = tarballDependencies[dependencyName];
if (section[dependencyName] !== tarballDependencies[dependencyName]) {
section[dependencyName] = tarballDependencies[dependencyName];
changed = true;
}
continue;
}

if (
typeof currentRange === "string"
&& currentRange.startsWith("file:")
&& publishedDependencyRanges.has(dependencyName)
) {
section[dependencyName] = publishedDependencyRanges.get(dependencyName);
changed = true;
}
}
}
packageJson.devDependencies = {
...(packageJson.devDependencies ?? {}),
...tarballDependencies,
};

fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, "utf8");

if (changed) {
fs.rmSync(path.join(directory, "package-lock.json"), { force: true });
fs.rmSync(path.join(directory, "node_modules"), { force: true, recursive: true });
}
}

function writeNode16Tsconfig(directory) {
Expand Down Expand Up @@ -443,6 +487,12 @@ function verifyPackedTarballInstall(packages) {

function verifyCoreGettingStarted(packages) {
const appDir = path.join(packageRoot, "rawsql-ts-getting-started");
const tarballDependencies = createTarballDependencyMap(packages);

if (!hasTarballDependency(tarballDependencies, "rawsql-ts")) {
return null;
}

ensureCleanDir(appDir);

writePackageJson(appDir, {
Expand All @@ -451,7 +501,7 @@ function verifyCoreGettingStarted(packages) {
version: "0.0.0",
type: "module",
devDependencies: {
"rawsql-ts": createTarballDependencyMap(packages)["rawsql-ts"],
"rawsql-ts": tarballDependencies["rawsql-ts"],
},
});

Expand Down Expand Up @@ -525,7 +575,7 @@ function verifyNpmPrimaryPath(packages) {
assertFileMissing(appDir, path.join("src", "features", "smoke", "queries", "smoke", "tests", "smoke.boundary.ztd.test.ts"), "phase-a default-scaffold-shape");
assertFileMissing(appDir, path.join(".ztd", "agents", "manifest.json"), "phase-a default-scaffold-shape");

restoreTarballDependencies(appDir, tarballDependencies);
restorePublishedDependencyRanges(appDir, packages);
runIn(appDir, NPM, ["install"]);

return { appDir };
Expand Down
Loading