@bobfrankston/importgen 0.1.23 → 0.1.24

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.
@@ -6,7 +6,9 @@
6
6
  "Bash(npx tsc)",
7
7
  "Bash(node index.js:*)",
8
8
  "Bash(dir:*)",
9
- "Bash(ls:*)"
9
+ "Bash(ls:*)",
10
+ "Bash(where extractids:*)",
11
+ "Bash(cd \"y:/dev/utils/importgen\" && npx tsc --noEmit 2>&1222)"
10
12
  ]
11
13
  }
12
14
  }
package/index.js CHANGED
@@ -135,6 +135,7 @@ function collectDependencies(packageJsonPath, visited, dependencies, htmlDir, no
135
135
  }
136
136
  catch (e) {
137
137
  console.error(`[generate-importmap] Error processing ${packageJsonPath}:`, e.message);
138
+ throw e; // Propagate so callers can avoid overwriting HTML with empty map
138
139
  }
139
140
  }
140
141
  function generateImportMap(packageJsonPath, htmlFilePath) {
@@ -146,7 +147,13 @@ function generateImportMap(packageJsonPath, htmlFilePath) {
146
147
  const depPaths = new Set();
147
148
  const warnings = [];
148
149
  // Recursively collect all dependencies
149
- collectDependencies(packageJsonPath, visited, dependencies, htmlDir, './node_modules', warnings, depPaths);
150
+ try {
151
+ collectDependencies(packageJsonPath, visited, dependencies, htmlDir, './node_modules', warnings, depPaths);
152
+ }
153
+ catch {
154
+ // Root package.json unreadable (e.g., mid-write) — skip update to preserve existing HTML
155
+ return result;
156
+ }
150
157
  result.depDirs = Array.from(depPaths);
151
158
  // Convert Map to plain object for JSON
152
159
  const imports = {};
@@ -188,6 +195,88 @@ function generateImportMap(packageJsonPath, htmlFilePath) {
188
195
  }
189
196
  return result;
190
197
  }
198
+ /**
199
+ * Freeze dependencies: replace junctions/symlinks in node_modules with real copies.
200
+ * Also adds a preinstall guard to package.json to prevent npm install from undoing the freeze.
201
+ */
202
+ function freezeDependencies(packageJsonPath) {
203
+ const nodeModulesDir = path.join(process.cwd(), 'node_modules');
204
+ if (!fs.existsSync(nodeModulesDir)) {
205
+ console.error('[importgen] Error: node_modules not found');
206
+ process.exit(1);
207
+ }
208
+ /** Check if a directory entry is a junction or symlink by comparing realpath to its path */
209
+ function isLinked(entryPath) {
210
+ try {
211
+ const stat = fs.lstatSync(entryPath);
212
+ // Symlink check (works on Linux/Mac symlinks)
213
+ if (stat.isSymbolicLink())
214
+ return true;
215
+ // Junction check (Windows): realpath differs from the entry path
216
+ if (stat.isDirectory()) {
217
+ const real = fs.realpathSync(entryPath);
218
+ return path.resolve(real) !== path.resolve(entryPath);
219
+ }
220
+ }
221
+ catch (e) {
222
+ console.error(`[importgen] Error checking ${entryPath}: ${e.message}`);
223
+ }
224
+ return false;
225
+ }
226
+ /** Freeze a single entry if it's a junction/symlink */
227
+ function freezeEntry(entryPath, displayName) {
228
+ if (!isLinked(entryPath))
229
+ return;
230
+ const target = fs.realpathSync(entryPath);
231
+ console.log(` Freezing ${displayName} (${target})`);
232
+ fs.rmSync(entryPath, { recursive: true });
233
+ fs.cpSync(target, entryPath, { recursive: true });
234
+ }
235
+ console.log('[importgen] Freezing dependencies...');
236
+ let frozenCount = 0;
237
+ const entries = fs.readdirSync(nodeModulesDir);
238
+ for (const entry of entries) {
239
+ const entryPath = path.join(nodeModulesDir, entry);
240
+ if (entry.startsWith('@')) {
241
+ // Scoped package — walk one level deeper
242
+ if (!fs.statSync(entryPath).isDirectory())
243
+ continue;
244
+ const scopedEntries = fs.readdirSync(entryPath);
245
+ for (const scoped of scopedEntries) {
246
+ const scopedPath = path.join(entryPath, scoped);
247
+ const before = isLinked(scopedPath);
248
+ freezeEntry(scopedPath, `${entry}/${scoped}`);
249
+ if (before)
250
+ frozenCount++;
251
+ }
252
+ }
253
+ else {
254
+ const before = isLinked(entryPath);
255
+ freezeEntry(entryPath, entry);
256
+ if (before)
257
+ frozenCount++;
258
+ }
259
+ }
260
+ console.log(`[importgen] Frozen ${frozenCount} linked packages`);
261
+ // Add preinstall guard to package.json
262
+ try {
263
+ const pkgRaw = fs.readFileSync(packageJsonPath, 'utf-8');
264
+ const pkg = JSON.parse(pkgRaw);
265
+ if (!pkg.scripts)
266
+ pkg.scripts = {};
267
+ if (pkg.scripts.preinstall) {
268
+ console.warn('[importgen] Warning: preinstall script already exists, skipping guard');
269
+ }
270
+ else {
271
+ pkg.scripts.preinstall = 'echo FROZEN: dependencies were frozen by importgen. Remove this script before running npm install. && exit 1';
272
+ fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
273
+ console.log('[importgen] Added preinstall guard to package.json');
274
+ }
275
+ }
276
+ catch (e) {
277
+ console.error(`[importgen] Error updating package.json: ${e.message}`);
278
+ }
279
+ }
191
280
  // Parse CLI arguments
192
281
  const args = process.argv.slice(2);
193
282
  if (args.includes('-v') || args.includes('--version')) {
@@ -195,6 +284,7 @@ if (args.includes('-v') || args.includes('--version')) {
195
284
  process.exit(0);
196
285
  }
197
286
  const watchMode = args.includes('-w') || args.includes('--watch');
287
+ const freezeMode = args.includes('--freeze');
198
288
  const htmlArg = args.find(a => !a.startsWith('-'));
199
289
  const packageJsonPath = path.join(process.cwd(), 'package.json');
200
290
  // Resolve HTML file: explicit argument, or search for common names
@@ -206,6 +296,10 @@ if (!fs.existsSync(packageJsonPath)) {
206
296
  console.error('[generate-importmap] Error: package.json not found in current directory');
207
297
  process.exit(1);
208
298
  }
299
+ if (freezeMode) {
300
+ freezeDependencies(packageJsonPath);
301
+ process.exit(0);
302
+ }
209
303
  if (!htmlFilePath || !fs.existsSync(htmlFilePath)) {
210
304
  if (htmlArg) {
211
305
  console.error(`[generate-importmap] Error: ${htmlArg} not found in current directory`);
@@ -240,9 +334,14 @@ if (watchMode) {
240
334
  watchedDepDirs = newDirs;
241
335
  }
242
336
  syncWatchedDeps(result.depDirs);
337
+ let debounceTimer = null;
243
338
  const onChange = () => {
244
- result = generateImportMap(packageJsonPath, htmlFilePath);
245
- syncWatchedDeps(result.depDirs);
339
+ if (debounceTimer)
340
+ clearTimeout(debounceTimer);
341
+ debounceTimer = setTimeout(() => {
342
+ result = generateImportMap(packageJsonPath, htmlFilePath);
343
+ syncWatchedDeps(result.depDirs);
344
+ }, 100);
246
345
  };
247
346
  watcher.on('change', onChange);
248
347
  watcher.on('add', onChange);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/importgen",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "description": "Generate ES Module import maps from package.json dependencies for native browser module loading",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -12,8 +12,10 @@
12
12
  "watch": "tsc -w",
13
13
  "preversion": "npm run build",
14
14
  "postversion": "echo Version updated to $(node -p \"require('./package.json').version\")",
15
- "release": "git add -A && git diff-index --quiet HEAD || git commit -m 'Build for release' && npm version patch",
16
- "installer": "npm run release && npm install -g ."
15
+ "release": "npmglobalize",
16
+ "installer": "npm run release && npm install -g .",
17
+ "old-release": "git add -A && git diff-index --quiet HEAD || git commit -m 'Build for release' && npm version patch",
18
+ "old-installer": "npm run release && npm install -g ."
17
19
  },
18
20
  "keywords": [
19
21
  "import-map",