@podosoft/podokit 0.12.1 → 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,20 +126,48 @@ function planUpdate(projectRoot, templatesDir) {
124
126
  changes,
125
127
  };
126
128
  }
127
- function assertPreviousExternalModulesInstalled(projectRoot, templatesDir, modules) {
129
+ function assertRecordedExternalModules(moduleRoot, templatesDir, modules) {
128
130
  const modulesDir = (0, node_path_1.join)(templatesDir, "modules");
129
131
  for (const module of modules) {
130
132
  if (!module.packageName || !module.moduleVersion)
131
133
  continue;
132
- const resolved = (0, add_1.resolveModule)(module.packageName, modulesDir, projectRoot);
133
- if (!resolved?.moduleVersion || resolved.moduleVersion === module.moduleVersion)
134
+ const resolved = (0, add_1.resolveModule)(module.packageName, modulesDir, moduleRoot);
135
+ if (resolved?.moduleVersion === module.moduleVersion)
134
136
  continue;
135
- throw new Error(`Cannot use --from while external module "${module.packageName}" is installed at ` +
136
- `${resolved.moduleVersion} but the project records ${module.moduleVersion}. Restore ` +
137
- `${module.packageName}@${module.moduleVersion}, update PodoKit with --from, then upgrade ` +
138
- "external modules separately.");
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.`);
139
140
  }
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
+ }
141
171
  function writeFile(projectRoot, path, content) {
142
172
  const abs = (0, node_path_1.join)(projectRoot, path);
143
173
  (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(abs), { recursive: true });
@@ -203,18 +233,23 @@ function applyUpdate(projectRoot, templatesDir, options = {}) {
203
233
  const managedOverrides = targetManagedOverrides(projectRoot, templatesDir, modules, manifest.managedOverrides);
204
234
  const ownedGlobs = targetOwnedGlobs(projectRoot, templatesDir, modules, manifest.ownedGlobs);
205
235
  const needsMergeBase = plan.changes.some((change) => change.action === "conflict");
236
+ let oldTree = null;
206
237
  if (options.oldTemplatesDir && needsMergeBase) {
207
- assertPreviousExternalModulesInstalled(projectRoot, templatesDir, modules);
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
+ }
208
252
  }
209
- const oldTree = options.oldTemplatesDir && needsMergeBase
210
- ? (0, assemble_1.assembleProject)({
211
- templatesDir: options.oldTemplatesDir,
212
- template: manifest.template,
213
- answers: manifest.answers,
214
- modules,
215
- projectRoot,
216
- })
217
- : null;
218
253
  const result = { written: [], removed: [], merged: [], conflicts: [] };
219
254
  for (const change of plan.changes) {
220
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.1",
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": {