pob 39.1.0 → 39.1.2

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.
@@ -111,7 +111,7 @@ export default class CoreGitGithubGenerator extends Generator {
111
111
  });
112
112
  }
113
113
 
114
- async configureProtectionRule(owner, repo) {
114
+ async #configureProtectionRule(owner, repo) {
115
115
  if (!this.options.ciEnabled) return;
116
116
  if (ciContexts.length === 0) {
117
117
  console.warn("No ci contexts: branch protection not configured");
@@ -166,7 +166,7 @@ export default class CoreGitGithubGenerator extends Generator {
166
166
  * Runs a step of the repository setup, printing which step failed and what
167
167
  * to run by hand instead of stopping the run.
168
168
  */
169
- runStep(description, manualCommand, fn) {
169
+ #runStep(description, manualCommand, fn) {
170
170
  try {
171
171
  fn();
172
172
  return true;
@@ -179,7 +179,7 @@ export default class CoreGitGithubGenerator extends Generator {
179
179
  }
180
180
  }
181
181
 
182
- async createRepository(owner, repo, pkg) {
182
+ async #createRepository(owner, repo, pkg) {
183
183
  const cwd = this.destinationPath();
184
184
 
185
185
  try {
@@ -210,7 +210,7 @@ export default class CoreGitGithubGenerator extends Generator {
210
210
  console.warn(
211
211
  `github repository ${owner}/${repo} already exists, syncing its settings`,
212
212
  );
213
- await this.syncRepository(owner, repo, pkg);
213
+ await this.#syncRepository(owner, repo, pkg);
214
214
  }
215
215
 
216
216
  const hasCommit =
@@ -221,7 +221,7 @@ export default class CoreGitGithubGenerator extends Generator {
221
221
  }).exitCode === 0;
222
222
 
223
223
  if (!hasCommit) {
224
- const committed = this.runStep(
224
+ const committed = this.#runStep(
225
225
  "create the initial commit",
226
226
  'git add --all . && git commit -m "chore: initial commit [skip ci]"',
227
227
  () => {
@@ -238,7 +238,7 @@ export default class CoreGitGithubGenerator extends Generator {
238
238
  if (!committed) return;
239
239
  }
240
240
 
241
- const pushed = this.runStep(
241
+ const pushed = this.#runStep(
242
242
  "push to github",
243
243
  "git branch -M main && git push -u origin main",
244
244
  () => {
@@ -248,10 +248,10 @@ export default class CoreGitGithubGenerator extends Generator {
248
248
  );
249
249
  if (!pushed) return;
250
250
 
251
- await this.configureProtectionRule(owner, repo);
251
+ await this.#configureProtectionRule(owner, repo);
252
252
  }
253
253
 
254
- async syncRepository(owner, repo, pkg) {
254
+ async #syncRepository(owner, repo, pkg) {
255
255
  try {
256
256
  await githubRequest("PATCH", `repos/${owner}/${repo}`, {
257
257
  name: repo,
@@ -282,11 +282,11 @@ export default class CoreGitGithubGenerator extends Generator {
282
282
  }
283
283
 
284
284
  if (this.options.shouldCreate) {
285
- await this.createRepository(owner, repo, pkg);
285
+ await this.#createRepository(owner, repo, pkg);
286
286
  } else {
287
287
  console.log("sync github info");
288
- await this.syncRepository(owner, repo, pkg);
289
- await this.configureProtectionRule(owner, repo);
288
+ await this.#syncRepository(owner, repo, pkg);
289
+ await this.#configureProtectionRule(owner, repo);
290
290
  }
291
291
  }
292
292
  }
@@ -1,8 +1,45 @@
1
+ import { execFileSync } from "node:child_process";
1
2
  import sortObject from "@pob/sort-object";
2
- import { FAILSAFE_SCHEMA, dump, loadAll } from "js-yaml";
3
+ import { FAILSAFE_SCHEMA, dump, load, loadAll } from "js-yaml";
4
+ import { satisfies } from "semver";
3
5
  import Generator from "yeoman-generator";
4
6
  import { writeAndFormat } from "../../../utils/writeAndFormat.js";
5
7
 
8
+ const pnpmVersionRange = "^12.0.0";
9
+
10
+ // pnpm 12 writes the pnpm version in a first document of pnpm-lock.yaml when
11
+ // devEngines.packageManager is not ignored. Removes it when it has nothing
12
+ // else (no config dependencies).
13
+ const removePackageManagerLockfileDocument = (lockfile) => {
14
+ if (!lockfile.startsWith("---\n")) return lockfile;
15
+ const end = lockfile.indexOf("\n---\n", 4);
16
+ if (end === -1) return lockfile;
17
+ const envDocument = load(lockfile.slice(4, end), { schema: FAILSAFE_SCHEMA });
18
+ const importer = envDocument?.importers?.["."] ?? {};
19
+ const hasOtherEntries = Object.keys(importer).some(
20
+ (key) =>
21
+ key !== "packageManagerDependencies" && key !== "configDependencies",
22
+ );
23
+ if (
24
+ hasOtherEntries ||
25
+ Object.keys(importer.configDependencies ?? {}).length > 0
26
+ ) {
27
+ return lockfile;
28
+ }
29
+ return lockfile.slice(end + "\n---\n".length);
30
+ };
31
+
32
+ const getRunningPnpmVersion = (cwd) => {
33
+ try {
34
+ return execFileSync("pnpm", ["--version"], {
35
+ cwd,
36
+ encoding: "utf8",
37
+ }).trim();
38
+ } catch {
39
+ return null;
40
+ }
41
+ };
42
+
6
43
  const minimumReleaseAgeExcludePackages = [
7
44
  "@pob/*",
8
45
  "pob",
@@ -38,19 +75,26 @@ export default class CorePnpmGenerator extends Generator {
38
75
  const pkg = this.fs.readJSON(this.destinationPath("package.json"));
39
76
 
40
77
  if (this.options.enable) {
41
- // devEngines.packageManager replaces packageManager: pnpm switches to
42
- // this version on startup (including when pnpm install is run by pob
43
- // with an older pnpm), and pnpm/action-setup reads it.
78
+ // devEngines.packageManager is read by pnpm/action-setup. onFail is
79
+ // "ignore": otherwise pnpm writes its version in pnpm-lock.yaml and it
80
+ // has to be updated manually. The version is checked by engines.pnpm.
44
81
  delete pkg.packageManager;
45
82
  pkg.devEngines = {
46
83
  ...pkg.devEngines,
47
84
  packageManager: {
48
85
  name: "pnpm",
49
- version: "^12.0.0",
50
- onFail: "download",
86
+ version: pnpmVersionRange,
87
+ onFail: "ignore",
51
88
  },
52
89
  };
53
90
 
91
+ const lockfilePath = this.destinationPath("pnpm-lock.yaml");
92
+ if (this.fs.exists(lockfilePath)) {
93
+ const lockfile = this.fs.read(lockfilePath);
94
+ const newLockfile = removePackageManagerLockfileDocument(lockfile);
95
+ if (newLockfile !== lockfile) this.fs.write(lockfilePath, newLockfile);
96
+ }
97
+
54
98
  const configString = this.fs.read(
55
99
  this.destinationPath("pnpm-workspace.yaml"),
56
100
  { defaults: "" },
@@ -118,11 +162,23 @@ export default class CorePnpmGenerator extends Generator {
118
162
  `pnpm-workspace.yaml: set allowBuilds to true or false for ${this.pendingAllowBuilds.join(", ")}`,
119
163
  );
120
164
  }
165
+ // the running pnpm can be older than the version required by pob, for
166
+ // example in the @pob/root renovate workflow after a pnpm major update
167
+ const runningPnpmVersion = getRunningPnpmVersion(this.destinationPath());
168
+ const pnpmArgs =
169
+ runningPnpmVersion && satisfies(runningPnpmVersion, pnpmVersionRange)
170
+ ? []
171
+ : ["with", pnpmVersionRange];
172
+
121
173
  // pob just modified package.json: the lockfile must be allowed to update,
122
174
  // in particular in the automatic update GitHub Actions workflow (CI
123
175
  // defaults to frozen lockfile)
124
- this.spawnSync("pnpm", ["install", "--no-frozen-lockfile"], {});
125
- this.spawnSync("pnpm", ["dedupe"], {});
176
+ this.spawnSync(
177
+ "pnpm",
178
+ [...pnpmArgs, "install", "--no-frozen-lockfile"],
179
+ {},
180
+ );
181
+ this.spawnSync("pnpm", [...pnpmArgs, "dedupe"], {});
126
182
 
127
183
  this.fs.delete("package-lock.json");
128
184
  this.fs.delete("yarn.lock");
@@ -131,11 +187,11 @@ export default class CorePnpmGenerator extends Generator {
131
187
 
132
188
  if (pkg.scripts?.preversion) {
133
189
  try {
134
- this.spawnSync("pnpm", ["run", "preversion"]);
190
+ this.spawnSync("pnpm", [...pnpmArgs, "run", "preversion"]);
135
191
  } catch {}
136
192
  } else if (pkg.scripts?.build) {
137
193
  try {
138
- this.spawnSync("pnpm", ["run", "build"]);
194
+ this.spawnSync("pnpm", [...pnpmArgs, "run", "build"]);
139
195
  } catch {}
140
196
  }
141
197
  }
@@ -72,7 +72,7 @@ export default class MonorepoTypescriptGenerator extends Generator {
72
72
  // linting and tsc -b: root config files (e.g. vitest.config.ts), a root
73
73
  // scripts/ dir, and each package's scripts/ dir (matched via the workspace
74
74
  // globs so all packages are covered)
75
- hasTools() {
75
+ #hasTools() {
76
76
  return (
77
77
  hasRootConfigFiles(this.destinationPath()) ||
78
78
  existsSync(this.destinationPath("scripts"))
@@ -81,8 +81,8 @@ export default class MonorepoTypescriptGenerator extends Generator {
81
81
 
82
82
  // tsc fails on a tsconfig.json without files nor references, like the one of
83
83
  // a new monorepo without packages
84
- hasTypescriptProjects() {
85
- return JSON.parse(this.options.packagePaths).length > 0 || this.hasTools();
84
+ #hasTypescriptProjects() {
85
+ return JSON.parse(this.options.packagePaths).length > 0 || this.#hasTools();
86
86
  }
87
87
 
88
88
  writing() {
@@ -109,7 +109,7 @@ export default class MonorepoTypescriptGenerator extends Generator {
109
109
  );
110
110
 
111
111
  if (this.options.enable) {
112
- packageUtils.addOrRemoveScripts(pkg, this.hasTypescriptProjects(), {
112
+ packageUtils.addOrRemoveScripts(pkg, this.#hasTypescriptProjects(), {
113
113
  tsc: "tsc -b",
114
114
  });
115
115
  packageUtils.addOrRemoveScripts(
@@ -161,7 +161,7 @@ export default class MonorepoTypescriptGenerator extends Generator {
161
161
  this.fs.delete(this.destinationPath("tsconfig.root-configs.json"));
162
162
  this.fs.delete(tsconfigTestPath);
163
163
 
164
- if (!this.options.enable || !this.hasTypescriptProjects()) {
164
+ if (!this.options.enable || !this.#hasTypescriptProjects()) {
165
165
  this.fs.delete(tsconfigPath);
166
166
  this.fs.delete(tsconfigCheckPath);
167
167
  this.fs.delete(tsconfigBuildPath);
@@ -171,7 +171,7 @@ export default class MonorepoTypescriptGenerator extends Generator {
171
171
  const pkg = this.fs.readJSON(this.destinationPath("package.json"));
172
172
 
173
173
  const workspaceGlobs = pkg.workspaces || [];
174
- const hasTools = this.hasTools();
174
+ const hasTools = this.#hasTools();
175
175
  if (hasTools) {
176
176
  await copyAndFormatTpl(
177
177
  this.fs,
@@ -85,8 +85,13 @@ export default class MonorepoWorkspacesGenerator extends Generator {
85
85
  if (!pkg.engines) pkg.engines = {};
86
86
  pkg.engines.yarn = "< 0.0.0";
87
87
  pkg.engines.npm = ">= 6.4.0";
88
+ } else if (this.options.packageManager === "pnpm") {
89
+ // checks the version locally: devEngines.packageManager is ignored by
90
+ // pnpm (see pob:core:pnpm)
91
+ if (!pkg.engines) pkg.engines = {};
92
+ pkg.engines.pnpm = ">= 12.0.0";
93
+ delete pkg.engines.yarn;
88
94
  } else if (pkg.engines) {
89
- // the pnpm version is set in devEngines by pob:core:pnpm
90
95
  delete pkg.engines.pnpm;
91
96
  delete pkg.engines.yarn;
92
97
  }
@@ -143,14 +143,14 @@ export default class PobBaseGenerator extends Generator {
143
143
  this.config.set("project", this.projectConfig);
144
144
  }
145
145
 
146
- getPackageManager() {
146
+ #getPackageManager() {
147
147
  return inMonorepo && !inMonorepo.root
148
148
  ? inMonorepo.rootPackageManager
149
149
  : (this.projectConfig?.packageManager ?? "yarn");
150
150
  }
151
151
 
152
152
  default() {
153
- const packageManager = this.getPackageManager();
153
+ const packageManager = this.#getPackageManager();
154
154
 
155
155
  this.composeWith("pob:core:bun", {
156
156
  type: this.projectConfig.type,
@@ -298,7 +298,7 @@ export default class PobBaseGenerator extends Generator {
298
298
  install() {
299
299
  if (this.options.fromPob) return;
300
300
 
301
- switch (this.getPackageManager()) {
301
+ switch (this.#getPackageManager()) {
302
302
  case "npm":
303
303
  this.spawnSync("npm", ["install"]);
304
304
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pob",
3
- "version": "39.1.0",
3
+ "version": "39.1.2",
4
4
  "description": "Pile of bones, library generator with git/babel/typescript/typedoc/readme/vitest",
5
5
  "keywords": [
6
6
  "skeleton"