pkg-pr-new 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +162 -44
- package/index.ts +119 -47
- package/package.json +2 -2
- package/template.ts +54 -0
- package/pkg-pr-new-0.0.7.tgz +0 -0
package/index.ts
CHANGED
|
@@ -6,21 +6,26 @@ import { createHash } from "node:crypto";
|
|
|
6
6
|
import { hash } from "ohash";
|
|
7
7
|
import fsSync from "fs";
|
|
8
8
|
import fs from "fs/promises";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { getPackageManifest, type PackageManifest } from "query-registry";
|
|
10
|
+
import type { Comment } from "@pkg-pr-new/utils";
|
|
11
|
+
import {
|
|
12
|
+
abbreviateCommitHash,
|
|
13
|
+
extractOwnerAndRepo,
|
|
14
|
+
extractRepository,
|
|
15
|
+
} from "@pkg-pr-new/utils";
|
|
12
16
|
import fg from "fast-glob";
|
|
13
17
|
import ignore from "ignore";
|
|
14
18
|
import "./environments";
|
|
15
19
|
import pkg from "./package.json" with { type: "json" };
|
|
16
20
|
import { isBinaryFile } from "isbinaryfile";
|
|
17
21
|
import { readPackageJSON, writePackageJSON } from "pkg-types";
|
|
22
|
+
import { createDefaultTemplate } from "./template";
|
|
18
23
|
|
|
19
24
|
declare global {
|
|
20
25
|
var API_URL: string;
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
const apiUrl = process.env.API_URL ?? API_URL
|
|
28
|
+
const apiUrl = process.env.API_URL ?? API_URL;
|
|
24
29
|
const publishUrl = new URL("/publish", apiUrl);
|
|
25
30
|
|
|
26
31
|
const main = defineCommand({
|
|
@@ -47,25 +52,32 @@ const main = defineCommand({
|
|
|
47
52
|
description:
|
|
48
53
|
"generate stackblitz templates out of directories in the current repo with the new built packages",
|
|
49
54
|
},
|
|
55
|
+
comment: {
|
|
56
|
+
type: "string", // "off", "create", "update" (default)
|
|
57
|
+
description: `"off" for no comments (silent mode). "create" for comment on each publish. "update" for one comment across the pull request with edits on each publish (default)`,
|
|
58
|
+
default: "update",
|
|
59
|
+
},
|
|
50
60
|
},
|
|
51
61
|
run: async ({ args }) => {
|
|
52
62
|
const paths = (args._.length ? args._ : ["."])
|
|
53
63
|
.flatMap((p) => (fg.isDynamicPattern(p) ? fg.sync(p) : p))
|
|
54
|
-
.map((p) => path.resolve(p));
|
|
64
|
+
.map((p) => path.resolve(p.trim()));
|
|
55
65
|
|
|
56
66
|
const templates = (
|
|
57
67
|
typeof args.template === "string"
|
|
58
68
|
? [args.template]
|
|
59
|
-
: ([...(args.template
|
|
69
|
+
: ([...(args.template || [])] as string[])
|
|
60
70
|
)
|
|
61
71
|
.flatMap((p) => (fg.isDynamicPattern(p) ? fg.sync(p) : p))
|
|
62
|
-
.map((p) => path.resolve(p));
|
|
72
|
+
.map((p) => path.resolve(p.trim()));
|
|
63
73
|
|
|
64
74
|
const formData = new FormData();
|
|
65
75
|
|
|
66
76
|
const isCompact = !!args.compact;
|
|
67
77
|
const isPnpm = !!args.pnpm;
|
|
68
78
|
|
|
79
|
+
const comment: Comment = args.comment as Comment;
|
|
80
|
+
|
|
69
81
|
if (!process.env.TEST && process.env.GITHUB_ACTIONS !== "true") {
|
|
70
82
|
console.error(
|
|
71
83
|
"Continuous Releases are only available in Github Actions.",
|
|
@@ -107,16 +119,23 @@ const main = defineCommand({
|
|
|
107
119
|
}
|
|
108
120
|
|
|
109
121
|
const { sha } = await checkResponse.json();
|
|
122
|
+
const abbreviatedSha = abbreviateCommitHash(sha);
|
|
110
123
|
|
|
111
124
|
const deps: Map<string, string> = new Map();
|
|
112
125
|
|
|
113
126
|
for (const p of paths) {
|
|
127
|
+
if (!(await hasPackageJson(p))) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
114
130
|
const pJsonPath = path.resolve(p, "package.json");
|
|
115
131
|
const pJson = await readPackageJSON(pJsonPath);
|
|
116
132
|
|
|
117
133
|
if (!pJson.name) {
|
|
118
134
|
throw new Error(`"name" field in ${pJsonPath} should be defined`);
|
|
119
135
|
}
|
|
136
|
+
if (pJson.private) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
120
139
|
|
|
121
140
|
if (isCompact) {
|
|
122
141
|
await verifyCompactMode(pJson.name);
|
|
@@ -125,19 +144,31 @@ const main = defineCommand({
|
|
|
125
144
|
deps.set(
|
|
126
145
|
pJson.name,
|
|
127
146
|
new URL(
|
|
128
|
-
`/${owner}/${repo}/${pJson.name}@${
|
|
147
|
+
`/${owner}/${repo}/${pJson.name}@${abbreviatedSha}`,
|
|
129
148
|
apiUrl,
|
|
130
149
|
).href,
|
|
131
150
|
);
|
|
132
151
|
}
|
|
133
152
|
|
|
134
153
|
for (const templateDir of templates) {
|
|
154
|
+
if (!(await hasPackageJson(templateDir))) {
|
|
155
|
+
console.log(
|
|
156
|
+
`skipping ${templateDir} because there's no package.json file`,
|
|
157
|
+
);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
135
160
|
const pJsonPath = path.resolve(templateDir, "package.json");
|
|
136
161
|
const pJson = await readPackageJSON(pJsonPath);
|
|
137
162
|
|
|
138
163
|
if (!pJson.name) {
|
|
139
164
|
throw new Error(`"name" field in ${pJsonPath} should be defined`);
|
|
140
165
|
}
|
|
166
|
+
if (pJson.private) {
|
|
167
|
+
console.log(
|
|
168
|
+
`skipping ${templateDir} because the package is private`,
|
|
169
|
+
);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
141
172
|
|
|
142
173
|
console.log("preparing template:", pJson.name);
|
|
143
174
|
|
|
@@ -174,16 +205,45 @@ const main = defineCommand({
|
|
|
174
205
|
await restore();
|
|
175
206
|
}
|
|
176
207
|
|
|
208
|
+
const noDefaultTemplate = args.template === false;
|
|
209
|
+
|
|
210
|
+
if (!templates.length && !noDefaultTemplate) {
|
|
211
|
+
const project = createDefaultTemplate(
|
|
212
|
+
Object.fromEntries(deps.entries()),
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
for (const filePath of Object.keys(project)) {
|
|
216
|
+
formData.append(
|
|
217
|
+
`template:default:${encodeURIComponent(filePath)}`,
|
|
218
|
+
project[filePath],
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
177
223
|
const restoreMap = new Map<
|
|
178
224
|
string,
|
|
179
225
|
Awaited<ReturnType<typeof writeDeps>>
|
|
180
226
|
>();
|
|
181
227
|
for (const p of paths) {
|
|
228
|
+
if (!(await hasPackageJson(p))) {
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
const pJsonPath = path.resolve(p, "package.json");
|
|
232
|
+
const pJson = await readPackageJSON(pJsonPath);
|
|
233
|
+
|
|
234
|
+
if (pJson.private) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
182
238
|
restoreMap.set(p, await writeDeps(p, deps));
|
|
183
239
|
}
|
|
184
240
|
|
|
185
241
|
const shasums: Record<string, string> = {};
|
|
186
242
|
for (const p of paths) {
|
|
243
|
+
if (!(await hasPackageJson(p))) {
|
|
244
|
+
console.log(`skipping ${p} because there's no package.json file`);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
187
247
|
const pJsonPath = path.resolve(p, "package.json");
|
|
188
248
|
try {
|
|
189
249
|
const pJson = await readPackageJSON(pJsonPath);
|
|
@@ -193,6 +253,10 @@ const main = defineCommand({
|
|
|
193
253
|
`"name" field in ${pJsonPath} should be defined`,
|
|
194
254
|
);
|
|
195
255
|
}
|
|
256
|
+
if (pJson.private) {
|
|
257
|
+
console.log(`skipping ${p} because the package is private`);
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
196
260
|
|
|
197
261
|
const { filename, shasum } = await resolveTarball(
|
|
198
262
|
isPnpm ? "pnpm" : "npm",
|
|
@@ -209,13 +273,14 @@ const main = defineCommand({
|
|
|
209
273
|
});
|
|
210
274
|
formData.append(`package:${pJson.name}`, blob, filename);
|
|
211
275
|
} finally {
|
|
212
|
-
await restoreMap.get(
|
|
276
|
+
await restoreMap.get(p)!();
|
|
213
277
|
}
|
|
214
278
|
}
|
|
215
279
|
|
|
216
280
|
const res = await fetch(publishUrl, {
|
|
217
281
|
method: "POST",
|
|
218
282
|
headers: {
|
|
283
|
+
"sb-comment": comment,
|
|
219
284
|
"sb-compact": `${isCompact}`,
|
|
220
285
|
"sb-key": key,
|
|
221
286
|
"sb-shasums": JSON.stringify(shasums),
|
|
@@ -256,30 +321,18 @@ runMain(main);
|
|
|
256
321
|
|
|
257
322
|
// TODO: we'll add support for yarn if users hit issues with npm
|
|
258
323
|
async function resolveTarball(pm: "npm" | "pnpm", p: string) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
stdio: "overlapped",
|
|
272
|
-
cwd: p,
|
|
273
|
-
});
|
|
274
|
-
const filename = stdout.trim();
|
|
275
|
-
|
|
276
|
-
const shasum = createHash("sha1")
|
|
277
|
-
.update(await fs.readFile(path.resolve(p, filename)))
|
|
278
|
-
.digest("hex");
|
|
279
|
-
|
|
280
|
-
return { filename, shasum };
|
|
281
|
-
}
|
|
282
|
-
throw new Error("Could not resolve package manager");
|
|
324
|
+
const { stdout } = await ezSpawn.async(`${pm} pack`, {
|
|
325
|
+
stdio: "overlapped",
|
|
326
|
+
cwd: p,
|
|
327
|
+
});
|
|
328
|
+
const lines = stdout.split("\n").filter(Boolean);
|
|
329
|
+
const filename = lines[lines.length - 1].trim();
|
|
330
|
+
|
|
331
|
+
const shasum = createHash("sha1")
|
|
332
|
+
.update(await fs.readFile(path.resolve(p, filename)))
|
|
333
|
+
.digest("hex");
|
|
334
|
+
|
|
335
|
+
return { filename, shasum };
|
|
283
336
|
}
|
|
284
337
|
|
|
285
338
|
async function writeDeps(p: string, deps: Map<string, string>) {
|
|
@@ -311,23 +364,42 @@ function hijackDeps(
|
|
|
311
364
|
}
|
|
312
365
|
|
|
313
366
|
async function verifyCompactMode(packageName: string) {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
Make sure to have your package on npm first or configure the 'repository' field in your package.json properly.`,
|
|
317
|
-
);
|
|
367
|
+
let manifest: PackageManifest;
|
|
368
|
+
|
|
318
369
|
try {
|
|
319
|
-
|
|
370
|
+
manifest = await getPackageManifest(packageName);
|
|
371
|
+
} catch {
|
|
372
|
+
throw new Error(
|
|
373
|
+
`pkg-pr-new cannot resolve ${packageName} from npm. --compact flag depends on the package being available in npm.
|
|
374
|
+
Make sure to have your package on npm first.`,
|
|
375
|
+
);
|
|
376
|
+
}
|
|
320
377
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
throw error;
|
|
324
|
-
}
|
|
378
|
+
const instruction = `Make sure to configure the 'repository' / 'repository.url' field in its package.json properly.
|
|
379
|
+
See https://docs.npmjs.com/cli/v10/configuring-npm/package-json#repository for details.`;
|
|
325
380
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
381
|
+
const repository = extractRepository(manifest);
|
|
382
|
+
if (!repository) {
|
|
383
|
+
throw new Error(
|
|
384
|
+
`pkg-pr-new cannot extract the repository link from the ${packageName} manifest. --compact flag requires the link to be present.
|
|
385
|
+
${instruction}`,
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const match = extractOwnerAndRepo(repository);
|
|
390
|
+
if (!match) {
|
|
391
|
+
throw new Error(
|
|
392
|
+
`pkg-pr-new cannot extract the owner and repo names from the ${packageName} repository link: ${repository}. --compact flag requires these names.
|
|
393
|
+
${instruction}`,
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async function hasPackageJson(p: string) {
|
|
399
|
+
try {
|
|
400
|
+
await fs.access(path.resolve(p, "package.json"), fs.constants.F_OK);
|
|
401
|
+
return true;
|
|
330
402
|
} catch {
|
|
331
|
-
|
|
403
|
+
return false;
|
|
332
404
|
}
|
|
333
405
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pkg-pr-new",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"ignore": "^5.3.1",
|
|
23
23
|
"isbinaryfile": "^5.0.2",
|
|
24
24
|
"pkg-types": "^1.1.1",
|
|
25
|
-
"query-registry": "^3.0.
|
|
25
|
+
"query-registry": "^3.0.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@pkg-pr-new/utils": "workspace:^",
|
package/template.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export const createDefaultTemplate = (
|
|
2
|
+
dependencies: Record<string, string>,
|
|
3
|
+
) => ({
|
|
4
|
+
"index.js": "",
|
|
5
|
+
"README.md": `
|
|
6
|
+
# Default Template
|
|
7
|
+
|
|
8
|
+
This is a template that leverages the experimental templates feature in the \`pkg.pr.new\` tool.
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
Templates are particularly useful for creating live, interactive examples of your packages, which can be very beneficial for both development and documentation purposes.
|
|
13
|
+
|
|
14
|
+
As a user, you can check the package.json file and see the new generated packages! You can just copy those and put them in your package.json or install them with your favorite package manager.
|
|
15
|
+
|
|
16
|
+
${Object.values(dependencies)
|
|
17
|
+
.map(
|
|
18
|
+
(url) => `
|
|
19
|
+
\`\`\`sh
|
|
20
|
+
npm i ${url}
|
|
21
|
+
\`\`\`
|
|
22
|
+
`,
|
|
23
|
+
)
|
|
24
|
+
.join("")}
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
To use this feature as a maintainer, you can run the following command:
|
|
29
|
+
|
|
30
|
+
\`\`\`sh
|
|
31
|
+
npx pkg-pr-new publish ./packages/A --template ./examples/*
|
|
32
|
+
\`\`\`
|
|
33
|
+
|
|
34
|
+
## Benefits
|
|
35
|
+
|
|
36
|
+
- Interactive Demos: Automatically create live demos that users can interact with directly in their browser.
|
|
37
|
+
- Enhanced Testing: Quickly spin up environments to test your package in different scenarios.
|
|
38
|
+
- Improved Sharing: Easily share working examples of your package with collaborators or users without needing them to set up their environment.
|
|
39
|
+
`,
|
|
40
|
+
"package.json": JSON.stringify(
|
|
41
|
+
{
|
|
42
|
+
name: "default",
|
|
43
|
+
version: "1.0.0",
|
|
44
|
+
description: "generated by pkg.pr.new",
|
|
45
|
+
main: "index.js",
|
|
46
|
+
dependencies,
|
|
47
|
+
keywords: [],
|
|
48
|
+
author: "pkg.pr.new",
|
|
49
|
+
license: "ISC",
|
|
50
|
+
},
|
|
51
|
+
null,
|
|
52
|
+
2,
|
|
53
|
+
),
|
|
54
|
+
});
|
package/pkg-pr-new-0.0.7.tgz
DELETED
|
Binary file
|