Skip to content

Commit 56ed524

Browse files
authored
Merge pull request #2707 from srod/develop
Merge develop: Fix workspace publishing and update security policy
2 parents c31bd59 + 4a5538a commit 56ed524

6 files changed

Lines changed: 227 additions & 6 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
"@node-minify/babel-minify": patch
3+
"@node-minify/clean-css": patch
4+
"@node-minify/cli": patch
5+
"@node-minify/core": patch
6+
"@node-minify/crass": patch
7+
"@node-minify/cssnano": patch
8+
"@node-minify/csso": patch
9+
"@node-minify/esbuild": patch
10+
"@node-minify/google-closure-compiler": patch
11+
"@node-minify/html-minifier": patch
12+
"@node-minify/jsonminify": patch
13+
"@node-minify/lightningcss": patch
14+
"@node-minify/no-compress": patch
15+
"@node-minify/oxc": patch
16+
"@node-minify/run": patch
17+
"@node-minify/sqwish": patch
18+
"@node-minify/swc": patch
19+
"@node-minify/terser": patch
20+
"@node-minify/types": patch
21+
"@node-minify/uglify-es": patch
22+
"@node-minify/uglify-js": patch
23+
"@node-minify/utils": patch
24+
"@node-minify/yui": patch
25+
---
26+
27+
Fix npm install error caused by unresolved workspace:\* references in published packages

.coderabbit.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
language: "en-US"
22
reviews:
33
profile: "chill"
4+
path_filters:
5+
- "!dist/**"
6+
- "!node_modules/**"
7+
- "!coverage/**"
8+
- "!tests/tmp/**"
9+
- "!*.tsbuildinfo"
10+
- "!.changeset/**"
11+
- "!examples/public/**/*-dist/**"
12+
- "!examples/public/**/*.min.*"
413
auto_review:
514
enabled: true
615
auto_incremental_review: true
716
base_branches:
8-
- develop
17+
- develop

.github/workflows/publish.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@ jobs:
2626

2727
- name: 📦 Setup Bun
2828
uses: oven-sh/setup-bun@v2
29+
with:
30+
bun-version: "1.3.5"
2931

3032
- name: ⚙️ Setup Node
3133
uses: actions/setup-node@v6
3234
with:
33-
node-version: '22'
35+
node-version: "22"
36+
registry-url: "https://registry.npmjs.org"
3437

3538
- name: 🔧 Upgrade npm for OIDC support
3639
run: npm install -g npm@latest
@@ -42,8 +45,8 @@ jobs:
4245
uses: changesets/action@v1
4346
id: changesets
4447
with:
45-
publish: bun changeset:release
46-
version: bun changeset:version
48+
publish: bun run changeset:release
49+
version: bun run changeset:version
4750
createGithubReleases: true
4851
env:
4952
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

SECURITY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
| Version | Supported |
66
| ------- | ------------------ |
7+
| 10.x | :white_check_mark: |
78
| 9.x | :white_check_mark: |
89
| < 9.0 | :x: |
910

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
"test:watch": "vitest",
3535
"test:coverage": "vitest run --coverage",
3636
"changeset": "changeset",
37-
"changeset:version": "changeset version && bun install --no-frozen-lockfile",
38-
"changeset:release": "bun run build && changeset publish --provenance",
37+
"changeset:version": "changeset version && bun update",
38+
"changeset:release": "bun run build && bun scripts/publish.ts",
3939
"format:check": "bun run --filter '*' format:check",
4040
"format": "biome check --write .",
4141
"typecheck": "bun run --filter '*' typecheck"

scripts/publish.ts

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
#!/usr/bin/env bun
2+
/*! node-minify - MIT Licensed */
3+
4+
import { execSync } from "node:child_process";
5+
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
6+
import { dirname, join } from "node:path";
7+
import { fileURLToPath } from "node:url";
8+
9+
interface PackageJson {
10+
name: string;
11+
version: string;
12+
private?: boolean;
13+
dependencies?: Record<string, string>;
14+
devDependencies?: Record<string, string>;
15+
peerDependencies?: Record<string, string>;
16+
optionalDependencies?: Record<string, string>;
17+
}
18+
19+
const __dirname = dirname(fileURLToPath(import.meta.url));
20+
const PACKAGES_DIR = join(__dirname, "..", "packages");
21+
22+
/**
23+
* List package directory names inside the packages directory that contain a package.json file.
24+
*
25+
* @returns A sorted (alphabetical) array of directory names under PACKAGES_DIR that contain a package.json
26+
*/
27+
function getPackageDirs(): string[] {
28+
return readdirSync(PACKAGES_DIR, { withFileTypes: true })
29+
.filter((entry) => entry.isDirectory())
30+
.filter((entry) =>
31+
existsSync(join(PACKAGES_DIR, entry.name, "package.json"))
32+
)
33+
.map((entry) => entry.name)
34+
.sort();
35+
}
36+
37+
/**
38+
* Read and parse the package.json file for a package located under the packages root.
39+
*
40+
* @param packageDir - The directory name of the package inside PACKAGES_DIR
41+
* @returns The parsed package.json as a `PackageJson`
42+
*/
43+
function readPackageJson(packageDir: string): PackageJson {
44+
const pkgPath = join(PACKAGES_DIR, packageDir, "package.json");
45+
return JSON.parse(readFileSync(pkgPath, "utf-8"));
46+
}
47+
48+
/**
49+
* Builds a map from each package's name to its version by reading package.json for all packages.
50+
*
51+
* @returns A Map where each key is a package name and each value is that package's version string.
52+
*/
53+
function buildVersionMap(): Map<string, string> {
54+
const versionMap = new Map<string, string>();
55+
56+
for (const dir of getPackageDirs()) {
57+
const pkg = readPackageJson(dir);
58+
versionMap.set(pkg.name, pkg.version);
59+
}
60+
61+
return versionMap;
62+
}
63+
64+
/**
65+
* Replace `workspace:` dependency specifiers with concrete versions from the provided map.
66+
*
67+
* Resolves any dependency versions that start with `workspace:` by looking up the package name in `versionMap`. If a package name is not found, throws an error to prevent publishing packages with unresolved workspace references.
68+
*
69+
* @param deps - The dependency map to resolve (may be `dependencies`, `devDependencies`, `peerDependencies`, or `optionalDependencies`); may be `undefined`.
70+
* @param versionMap - A mapping from package name to concrete version string used to replace `workspace:` specifiers.
71+
* @returns The resolved dependency map with all workspace references replaced, or `undefined` if `deps` was `undefined`.
72+
* @throws {Error} When a workspace reference cannot be resolved to a concrete version.
73+
*/
74+
function resolveDependencies(
75+
deps: Record<string, string> | undefined,
76+
versionMap: Map<string, string>
77+
): Record<string, string> | undefined {
78+
if (!deps) return deps;
79+
80+
const resolved: Record<string, string> = {};
81+
for (const [name, version] of Object.entries(deps)) {
82+
if (version.startsWith("workspace:")) {
83+
const actualVersion = versionMap.get(name);
84+
if (actualVersion) {
85+
resolved[name] = actualVersion;
86+
} else {
87+
throw new Error(
88+
`Cannot resolve workspace:* reference for ${name}. Package not found in version map.`
89+
);
90+
}
91+
} else {
92+
resolved[name] = version;
93+
}
94+
}
95+
96+
return resolved;
97+
}
98+
99+
/**
100+
* Publishes non-private workspace packages and creates changeset tags.
101+
*
102+
* For each package under the packages directory, resolves `workspace:` dependency references to actual package versions, writes the updated package.json, attempts `npm publish --access public --provenance`, restores the original package.json, and logs progress. After processing all packages, runs `changeset tag` to create git tags; publish or tag failures are logged but do not stop processing.
103+
*
104+
* @returns Nothing.
105+
*/
106+
function main() {
107+
const packageDirs = getPackageDirs();
108+
const versionMap = buildVersionMap();
109+
110+
console.log(`Found ${packageDirs.length} packages to publish\n`);
111+
112+
for (const dir of packageDirs) {
113+
const pkgPath = join(PACKAGES_DIR, dir, "package.json");
114+
const originalContent = readFileSync(pkgPath, "utf-8");
115+
const pkg: PackageJson = JSON.parse(originalContent);
116+
117+
if (pkg.private) {
118+
console.log(`Skipping private package: ${pkg.name}`);
119+
continue;
120+
}
121+
122+
pkg.dependencies = resolveDependencies(pkg.dependencies, versionMap);
123+
pkg.devDependencies = resolveDependencies(
124+
pkg.devDependencies,
125+
versionMap
126+
);
127+
pkg.peerDependencies = resolveDependencies(
128+
pkg.peerDependencies,
129+
versionMap
130+
);
131+
pkg.optionalDependencies = resolveDependencies(
132+
pkg.optionalDependencies,
133+
versionMap
134+
);
135+
136+
writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
137+
138+
console.log(`Publishing ${pkg.name}@${pkg.version}...`);
139+
140+
try {
141+
execSync("npm publish --access public --provenance", {
142+
cwd: join(PACKAGES_DIR, dir),
143+
stdio: "inherit",
144+
});
145+
console.log(`Published ${pkg.name}@${pkg.version}\n`);
146+
} catch (err) {
147+
if (err instanceof Error) {
148+
console.log(
149+
`Failed to publish ${pkg.name}@${pkg.version} (may already exist)\n`
150+
);
151+
}
152+
} finally {
153+
writeFileSync(pkgPath, originalContent);
154+
}
155+
}
156+
157+
console.log("\nCreating git tags...");
158+
try {
159+
execSync("changeset tag", {
160+
cwd: join(__dirname, ".."),
161+
stdio: "inherit",
162+
});
163+
} catch (err) {
164+
if (err instanceof Error) {
165+
console.log("Failed to create tags (may already exist)");
166+
}
167+
}
168+
169+
console.log("\nDone!");
170+
}
171+
172+
try {
173+
main();
174+
} catch (error) {
175+
if (error instanceof Error) {
176+
console.error("Publish failed:", error.message);
177+
} else {
178+
console.error("Publish failed:", error);
179+
}
180+
process.exit(1);
181+
}

0 commit comments

Comments
 (0)