@podosoft/podokit 0.12.0 → 0.12.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.
package/dist/update.d.ts CHANGED
@@ -28,6 +28,8 @@ export declare function planUpdate(projectRoot: string, templatesDir: string): U
28
28
  export interface ApplyOptions {
29
29
  /** Templates for the version the project is currently on, for 3-way merges. */
30
30
  oldTemplatesDir?: string;
31
+ /** Preinstalled previous external modules. Primarily useful for offline updates. */
32
+ oldExternalModulesRoot?: string;
31
33
  }
32
34
  export interface ApplyResult {
33
35
  written: string[];
package/dist/update.js CHANGED
@@ -3,7 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.planUpdate = planUpdate;
4
4
  exports.applyUpdate = applyUpdate;
5
5
  exports.summarize = summarize;
6
+ const node_child_process_1 = require("node:child_process");
6
7
  const node_fs_1 = require("node:fs");
8
+ const node_os_1 = require("node:os");
7
9
  const node_path_1 = require("node:path");
8
10
  const podokit_template_engine_1 = require("@podosoft/podokit-template-engine");
9
11
  const assemble_1 = require("./assemble");
@@ -124,6 +126,48 @@ function planUpdate(projectRoot, templatesDir) {
124
126
  changes,
125
127
  };
126
128
  }
129
+ function assertRecordedExternalModules(moduleRoot, templatesDir, modules) {
130
+ const modulesDir = (0, node_path_1.join)(templatesDir, "modules");
131
+ for (const module of modules) {
132
+ if (!module.packageName || !module.moduleVersion)
133
+ continue;
134
+ const resolved = (0, add_1.resolveModule)(module.packageName, modulesDir, moduleRoot);
135
+ if (resolved?.moduleVersion === module.moduleVersion)
136
+ continue;
137
+ const actual = resolved?.moduleVersion ?? "not installed";
138
+ throw new Error(`Previous external module root has ${module.packageName} at ${actual}; ` +
139
+ `expected ${module.moduleVersion} from the project manifest.`);
140
+ }
141
+ }
142
+ function previousExternalModules(projectRoot, templatesDir, modules, providedRoot) {
143
+ const external = modules.filter((module) => Boolean(module.packageName && module.moduleVersion));
144
+ if (!external.length)
145
+ return { root: projectRoot, cleanup: false };
146
+ if (providedRoot) {
147
+ assertRecordedExternalModules(providedRoot, templatesDir, external);
148
+ return { root: providedRoot, cleanup: false };
149
+ }
150
+ const currentMatchesRecorded = external.every((module) => {
151
+ const resolved = (0, add_1.resolveModule)(module.packageName, (0, node_path_1.join)(templatesDir, "modules"), projectRoot);
152
+ return resolved?.moduleVersion === module.moduleVersion;
153
+ });
154
+ if (currentMatchesRecorded)
155
+ return { root: projectRoot, cleanup: false };
156
+ const root = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "podokit-update-modules-"));
157
+ const devDependencies = Object.fromEntries(external.map((module) => [module.packageName, module.moduleVersion]));
158
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(root, "package.json"), `${JSON.stringify({ name: "podokit-update-modules", private: true, devDependencies }, null, 2)}\n`);
159
+ try {
160
+ (0, node_child_process_1.execFileSync)("npm", ["install", "--ignore-scripts", "--no-audit", "--no-fund", "--package-lock=false"], { cwd: root, stdio: "pipe" });
161
+ assertRecordedExternalModules(root, templatesDir, external);
162
+ }
163
+ catch {
164
+ (0, node_fs_1.rmSync)(root, { recursive: true, force: true });
165
+ const expected = external.map((module) => `${module.packageName}@${module.moduleVersion}`).join(", ");
166
+ throw new Error(`Cannot install previous external modules (${expected}) for the 3-way merge. ` +
167
+ "Check npm registry access and try again.");
168
+ }
169
+ return { root, cleanup: true };
170
+ }
127
171
  function writeFile(projectRoot, path, content) {
128
172
  const abs = (0, node_path_1.join)(projectRoot, path);
129
173
  (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(abs), { recursive: true });
@@ -188,15 +232,24 @@ function applyUpdate(projectRoot, templatesDir, options = {}) {
188
232
  });
189
233
  const managedOverrides = targetManagedOverrides(projectRoot, templatesDir, modules, manifest.managedOverrides);
190
234
  const ownedGlobs = targetOwnedGlobs(projectRoot, templatesDir, modules, manifest.ownedGlobs);
191
- const oldTree = options.oldTemplatesDir
192
- ? (0, assemble_1.assembleProject)({
193
- templatesDir: options.oldTemplatesDir,
194
- template: manifest.template,
195
- answers: manifest.answers,
196
- modules,
197
- projectRoot,
198
- })
199
- : null;
235
+ const needsMergeBase = plan.changes.some((change) => change.action === "conflict");
236
+ let oldTree = null;
237
+ if (options.oldTemplatesDir && needsMergeBase) {
238
+ const previousModules = previousExternalModules(projectRoot, options.oldTemplatesDir, modules, options.oldExternalModulesRoot);
239
+ try {
240
+ oldTree = (0, assemble_1.assembleProject)({
241
+ templatesDir: options.oldTemplatesDir,
242
+ template: manifest.template,
243
+ answers: manifest.answers,
244
+ modules,
245
+ projectRoot: previousModules.root,
246
+ });
247
+ }
248
+ finally {
249
+ if (previousModules.cleanup)
250
+ (0, node_fs_1.rmSync)(previousModules.root, { recursive: true, force: true });
251
+ }
252
+ }
200
253
  const result = { written: [], removed: [], merged: [], conflicts: [] };
201
254
  for (const change of plan.changes) {
202
255
  const newText = treeText(newTree, change.path);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@podosoft/podokit",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "description": "An opinionated but extensible starter toolkit and CLI for full-stack TypeScript apps with NestJS, SvelteKit, TailwindCSS, shadcn-svelte, Docker, and k3s.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {