@bobfrankston/importgen 0.1.25 → 0.1.27
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/.claude/settings.local.json +3 -1
- package/README.md +15 -1
- package/index.js +73 -26
- package/package.json +2 -2
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"Bash(ls:*)",
|
|
10
10
|
"Bash(where extractids:*)",
|
|
11
11
|
"Bash(cd \"y:/dev/utils/importgen\" && npx tsc --noEmit 2>&1222)",
|
|
12
|
-
"Bash(cd y:/dev/utils/importgen && npm run release 2>&1exit)"
|
|
12
|
+
"Bash(cd y:/dev/utils/importgen && npm run release 2>&1exit)",
|
|
13
|
+
"Bash(cd y:/dev/utils/importgen && npx tsc --noEmit 2>&1)",
|
|
14
|
+
"Bash(cd:*)"
|
|
13
15
|
]
|
|
14
16
|
}
|
|
15
17
|
}
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ npm install -g @bobfrankston/importgen
|
|
|
25
25
|
Run from your project directory (containing `package.json` and your HTML file):
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
importgen [htmlfile] [--watch|-w] [--version|-v]
|
|
28
|
+
importgen [htmlfile] [--watch|-w] [--freeze] [--unfreeze] [--version|-v]
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
```bash
|
|
@@ -42,6 +42,12 @@ importgen default.htm --watch
|
|
|
42
42
|
|
|
43
43
|
# Show version
|
|
44
44
|
importgen -v
|
|
45
|
+
|
|
46
|
+
# Freeze: replace symlinks/junctions with real copies for deployment
|
|
47
|
+
importgen --freeze
|
|
48
|
+
|
|
49
|
+
# Unfreeze: restore symlinks and run npm install
|
|
50
|
+
importgen --unfreeze
|
|
45
51
|
```
|
|
46
52
|
|
|
47
53
|
`importgen` reads dependencies from `package.json` in the current directory and injects the import map into the HTML file.
|
|
@@ -129,6 +135,14 @@ Add a `.dependencies` field to `package.json` to override resolution paths for s
|
|
|
129
135
|
|
|
130
136
|
The import map will use the `.dependencies` path instead of the `node_modules` path for that package.
|
|
131
137
|
|
|
138
|
+
## Freeze / Unfreeze
|
|
139
|
+
|
|
140
|
+
`--freeze` prepares a project for deployment by replacing all symlinks and junctions in `node_modules` with real directory copies. This is useful when `file:` or `workspace:` dependencies point to local packages via symlinks that won't exist on a deployment target.
|
|
141
|
+
|
|
142
|
+
Freeze also adds a `preinstall` guard script to `package.json` that prevents accidental `npm install` from overwriting the frozen copies. If a `preinstall` script already exists, it is saved as `frozen-preinstall` so it can be restored later.
|
|
143
|
+
|
|
144
|
+
`--unfreeze` reverses the process: it removes the preinstall guard (restoring any original preinstall script), then runs `npm install` to re-establish normal symlinked dependencies.
|
|
145
|
+
|
|
132
146
|
## VS Code tasks.json Integration
|
|
133
147
|
|
|
134
148
|
Add `importgen --watch` as a background task that starts automatically when you open the project. Combined with `tsc --watch`, your import map stays current as you develop.
|
package/index.js
CHANGED
|
@@ -5,8 +5,13 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import fs from 'node:fs';
|
|
7
7
|
import path from 'node:path';
|
|
8
|
+
import { execSync } from 'node:child_process';
|
|
8
9
|
import chokidar from 'chokidar';
|
|
9
10
|
import packageJson from './package.json' with { type: 'json' };
|
|
11
|
+
/** Timestamp prefix for log messages */
|
|
12
|
+
function ts() {
|
|
13
|
+
return `[${new Date().toLocaleTimeString()}]`;
|
|
14
|
+
}
|
|
10
15
|
/**
|
|
11
16
|
* Resolve dependency path based on version specifier
|
|
12
17
|
*/
|
|
@@ -90,7 +95,7 @@ function collectDependencies(packageJsonPath, visited, dependencies, htmlDir, no
|
|
|
90
95
|
let depPath = resolveDependencyPath(packageDir, depName, versionToUse);
|
|
91
96
|
if (!depPath) {
|
|
92
97
|
const warning = `Could not follow path for ${depName} (${versionToUse})`;
|
|
93
|
-
console.warn(
|
|
98
|
+
console.warn(`${ts()} [generate-importmap] Warning: ${warning}`);
|
|
94
99
|
warnings.push(warning);
|
|
95
100
|
continue;
|
|
96
101
|
}
|
|
@@ -98,7 +103,7 @@ function collectDependencies(packageJsonPath, visited, dependencies, htmlDir, no
|
|
|
98
103
|
const dependencyBackupPath = depPath + '.dependency';
|
|
99
104
|
if (fs.existsSync(dependencyBackupPath)) {
|
|
100
105
|
const warning = `Using backup path ${dependencyBackupPath}`;
|
|
101
|
-
console.warn(
|
|
106
|
+
console.warn(`${ts()} [generate-importmap] Warning: ${warning}`);
|
|
102
107
|
warnings.push(warning);
|
|
103
108
|
depPath = dependencyBackupPath;
|
|
104
109
|
}
|
|
@@ -109,7 +114,7 @@ function collectDependencies(packageJsonPath, visited, dependencies, htmlDir, no
|
|
|
109
114
|
const depPackageJsonPath = path.join(depPath, 'package.json');
|
|
110
115
|
if (!fs.existsSync(depPackageJsonPath)) {
|
|
111
116
|
const warning = `Could not follow path for ${depName} - no package.json found at ${depPath}`;
|
|
112
|
-
console.warn(
|
|
117
|
+
console.warn(`${ts()} [generate-importmap] Warning: ${warning}`);
|
|
113
118
|
warnings.push(warning);
|
|
114
119
|
continue;
|
|
115
120
|
}
|
|
@@ -134,7 +139,7 @@ function collectDependencies(packageJsonPath, visited, dependencies, htmlDir, no
|
|
|
134
139
|
}
|
|
135
140
|
}
|
|
136
141
|
catch (e) {
|
|
137
|
-
console.error(
|
|
142
|
+
console.error(`${ts()} [generate-importmap] Error processing ${packageJsonPath}:`, e.message);
|
|
138
143
|
throw e; // Propagate so callers can avoid overwriting HTML with empty map
|
|
139
144
|
}
|
|
140
145
|
}
|
|
@@ -183,14 +188,14 @@ function generateImportMap(packageJsonPath, htmlFilePath) {
|
|
|
183
188
|
}
|
|
184
189
|
// Write back to HTML file
|
|
185
190
|
fs.writeFileSync(htmlFilePath, html, 'utf-8');
|
|
186
|
-
console.log(
|
|
191
|
+
console.log(`${ts()} [generate-importmap] Updated import map`);
|
|
187
192
|
console.log(` Scanned ${visited.size} dependencies, generated ${dependencies.size} entries`);
|
|
188
193
|
if (dependencies.size > 0) {
|
|
189
194
|
console.log(' Packages:', Array.from(dependencies.keys()).join(', '));
|
|
190
195
|
}
|
|
191
196
|
}
|
|
192
197
|
catch (e) {
|
|
193
|
-
console.error(
|
|
198
|
+
console.error(`${ts()} [generate-importmap] Error:`, e.message);
|
|
194
199
|
process.exit(1);
|
|
195
200
|
}
|
|
196
201
|
return result;
|
|
@@ -202,7 +207,7 @@ function generateImportMap(packageJsonPath, htmlFilePath) {
|
|
|
202
207
|
function freezeDependencies(packageJsonPath) {
|
|
203
208
|
const nodeModulesDir = path.join(process.cwd(), 'node_modules');
|
|
204
209
|
if (!fs.existsSync(nodeModulesDir)) {
|
|
205
|
-
console.error(
|
|
210
|
+
console.error(`${ts()} [importgen] Error: node_modules not found`);
|
|
206
211
|
process.exit(1);
|
|
207
212
|
}
|
|
208
213
|
/** Check if a directory entry is a junction or symlink by comparing realpath to its path */
|
|
@@ -219,7 +224,7 @@ function freezeDependencies(packageJsonPath) {
|
|
|
219
224
|
}
|
|
220
225
|
}
|
|
221
226
|
catch (e) {
|
|
222
|
-
console.error(
|
|
227
|
+
console.error(`${ts()} [importgen] Error checking ${entryPath}: ${e.message}`);
|
|
223
228
|
}
|
|
224
229
|
return false;
|
|
225
230
|
}
|
|
@@ -228,11 +233,11 @@ function freezeDependencies(packageJsonPath) {
|
|
|
228
233
|
if (!isLinked(entryPath))
|
|
229
234
|
return;
|
|
230
235
|
const target = fs.realpathSync(entryPath);
|
|
231
|
-
console.log(
|
|
236
|
+
console.log(`${ts()} Freezing ${displayName} (${target})`);
|
|
232
237
|
fs.rmSync(entryPath, { recursive: true });
|
|
233
238
|
fs.cpSync(target, entryPath, { recursive: true });
|
|
234
239
|
}
|
|
235
|
-
console.log(
|
|
240
|
+
console.log(`${ts()} [importgen] Freezing dependencies...`);
|
|
236
241
|
let frozenCount = 0;
|
|
237
242
|
const entries = fs.readdirSync(nodeModulesDir);
|
|
238
243
|
for (const entry of entries) {
|
|
@@ -257,7 +262,7 @@ function freezeDependencies(packageJsonPath) {
|
|
|
257
262
|
frozenCount++;
|
|
258
263
|
}
|
|
259
264
|
}
|
|
260
|
-
console.log(
|
|
265
|
+
console.log(`${ts()} [importgen] Frozen ${frozenCount} linked packages`);
|
|
261
266
|
// Add preinstall guard to package.json
|
|
262
267
|
try {
|
|
263
268
|
const pkgRaw = fs.readFileSync(packageJsonPath, 'utf-8');
|
|
@@ -265,16 +270,53 @@ function freezeDependencies(packageJsonPath) {
|
|
|
265
270
|
if (!pkg.scripts)
|
|
266
271
|
pkg.scripts = {};
|
|
267
272
|
if (pkg.scripts.preinstall) {
|
|
268
|
-
|
|
273
|
+
// Rename existing preinstall so unfreeze can restore it
|
|
274
|
+
pkg.scripts['frozen-preinstall'] = pkg.scripts.preinstall;
|
|
275
|
+
console.log(`${ts()} [importgen] Saved existing preinstall script as "frozen-preinstall"`);
|
|
276
|
+
}
|
|
277
|
+
pkg.scripts.preinstall = 'echo FROZEN: dependencies were frozen by importgen. Use importgen --unfreeze to restore. && exit 1';
|
|
278
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
279
|
+
console.log(`${ts()} [importgen] Added preinstall guard to package.json`);
|
|
280
|
+
}
|
|
281
|
+
catch (e) {
|
|
282
|
+
console.error(`${ts()} [importgen] Error updating package.json: ${e.message}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Unfreeze: remove preinstall guard, restore original preinstall if saved, then run npm install.
|
|
287
|
+
*/
|
|
288
|
+
function unfreezeDependencies(packageJsonPath) {
|
|
289
|
+
try {
|
|
290
|
+
const pkgRaw = fs.readFileSync(packageJsonPath, 'utf-8');
|
|
291
|
+
const pkg = JSON.parse(pkgRaw);
|
|
292
|
+
if (!pkg.scripts?.preinstall) {
|
|
293
|
+
console.log(`${ts()} [importgen] No preinstall guard found — nothing to unfreeze`);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (pkg.scripts['frozen-preinstall']) {
|
|
297
|
+
pkg.scripts.preinstall = pkg.scripts['frozen-preinstall'];
|
|
298
|
+
delete pkg.scripts['frozen-preinstall'];
|
|
299
|
+
console.log(`${ts()} [importgen] Restored original preinstall script`);
|
|
269
300
|
}
|
|
270
301
|
else {
|
|
271
|
-
pkg.scripts.preinstall
|
|
272
|
-
|
|
273
|
-
console.log('[importgen] Added preinstall guard to package.json');
|
|
302
|
+
delete pkg.scripts.preinstall;
|
|
303
|
+
console.log(`${ts()} [importgen] Removed preinstall guard`);
|
|
274
304
|
}
|
|
305
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
306
|
+
}
|
|
307
|
+
catch (e) {
|
|
308
|
+
console.error(`${ts()} [importgen] Error updating package.json: ${e.message}`);
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
// Run npm install to restore proper dependencies
|
|
312
|
+
console.log(`${ts()} [importgen] Running npm install...`);
|
|
313
|
+
try {
|
|
314
|
+
execSync('npm install', { stdio: 'inherit', cwd: process.cwd() });
|
|
315
|
+
console.log(`${ts()} [importgen] Unfreeze complete`);
|
|
275
316
|
}
|
|
276
317
|
catch (e) {
|
|
277
|
-
console.error(
|
|
318
|
+
console.error(`${ts()} [importgen] npm install failed: ${e.message}`);
|
|
319
|
+
process.exit(1);
|
|
278
320
|
}
|
|
279
321
|
}
|
|
280
322
|
// Parse CLI arguments
|
|
@@ -283,20 +325,21 @@ if (args.includes('-v') || args.includes('--version')) {
|
|
|
283
325
|
console.log(`importgen ${packageJson.version}`);
|
|
284
326
|
process.exit(0);
|
|
285
327
|
}
|
|
286
|
-
const knownFlags = new Set(['-w', '--watch', '--freeze', '-freeze', '-v', '--version']);
|
|
328
|
+
const knownFlags = new Set(['-w', '--watch', '--freeze', '-freeze', '--unfreeze', '-unfreeze', '-v', '--version']);
|
|
287
329
|
// Report unrecognized flags as errors
|
|
288
330
|
const unknownArgs = args.filter(a => a.startsWith('-') && !knownFlags.has(a));
|
|
289
331
|
if (unknownArgs.length > 0) {
|
|
290
|
-
console.error(
|
|
291
|
-
console.error(` Usage: importgen [htmlfile] [-w|--watch] [--freeze] [-v|--version]`);
|
|
332
|
+
console.error(`${ts()} [importgen] Error: unrecognized argument(s): ${unknownArgs.join(', ')}`);
|
|
333
|
+
console.error(` Usage: importgen [htmlfile] [-w|--watch] [--freeze] [--unfreeze] [-v|--version]`);
|
|
292
334
|
process.exit(1);
|
|
293
335
|
}
|
|
294
336
|
const watchMode = args.includes('-w') || args.includes('--watch');
|
|
295
337
|
const freezeMode = args.includes('--freeze') || args.includes('-freeze');
|
|
338
|
+
const unfreezeMode = args.includes('--unfreeze') || args.includes('-unfreeze');
|
|
296
339
|
const positionalArgs = args.filter(a => !a.startsWith('-'));
|
|
297
340
|
if (positionalArgs.length > 1) {
|
|
298
|
-
console.error(
|
|
299
|
-
console.error(` Usage: importgen [htmlfile] [-w|--watch] [--freeze] [-v|--version]`);
|
|
341
|
+
console.error(`${ts()} [importgen] Error: too many arguments: ${positionalArgs.join(', ')}`);
|
|
342
|
+
console.error(` Usage: importgen [htmlfile] [-w|--watch] [--freeze] [--unfreeze] [-v|--version]`);
|
|
300
343
|
process.exit(1);
|
|
301
344
|
}
|
|
302
345
|
const htmlArg = positionalArgs[0];
|
|
@@ -307,19 +350,23 @@ const htmlFileName = htmlArg || possibleHtmlFiles.find(f => fs.existsSync(path.j
|
|
|
307
350
|
const htmlFilePath = htmlFileName ? path.join(process.cwd(), htmlFileName) : null;
|
|
308
351
|
// Check if files exist
|
|
309
352
|
if (!fs.existsSync(packageJsonPath)) {
|
|
310
|
-
console.error(
|
|
353
|
+
console.error(`${ts()} [generate-importmap] Error: package.json not found in current directory`);
|
|
311
354
|
process.exit(1);
|
|
312
355
|
}
|
|
313
356
|
if (freezeMode) {
|
|
314
357
|
freezeDependencies(packageJsonPath);
|
|
315
358
|
process.exit(0);
|
|
316
359
|
}
|
|
360
|
+
if (unfreezeMode) {
|
|
361
|
+
unfreezeDependencies(packageJsonPath);
|
|
362
|
+
process.exit(0);
|
|
363
|
+
}
|
|
317
364
|
if (!htmlFilePath || !fs.existsSync(htmlFilePath)) {
|
|
318
365
|
if (htmlArg) {
|
|
319
|
-
console.error(
|
|
366
|
+
console.error(`${ts()} [generate-importmap] Error: ${htmlArg} not found in current directory`);
|
|
320
367
|
}
|
|
321
368
|
else {
|
|
322
|
-
console.error(
|
|
369
|
+
console.error(`${ts()} [generate-importmap] Error: No HTML file found. Looking for: ${possibleHtmlFiles.join(', ')}`);
|
|
323
370
|
}
|
|
324
371
|
process.exit(1);
|
|
325
372
|
}
|
|
@@ -362,7 +409,7 @@ if (watchMode) {
|
|
|
362
409
|
watcher.on('unlink', onChange);
|
|
363
410
|
watcher.on('ready', () => {
|
|
364
411
|
const total = 1 + watchedDepDirs.size;
|
|
365
|
-
console.log(
|
|
412
|
+
console.log(`${ts()} [generate-importmap] Watching ${total} directories for changes...`);
|
|
366
413
|
if (watchedDepDirs.size > 0) {
|
|
367
414
|
for (const dir of watchedDepDirs) {
|
|
368
415
|
console.log(` ${dir}`);
|
|
@@ -370,7 +417,7 @@ if (watchMode) {
|
|
|
370
417
|
}
|
|
371
418
|
});
|
|
372
419
|
watcher.on('error', (error) => {
|
|
373
|
-
console.error(
|
|
420
|
+
console.error(`${ts()} [generate-importmap] Watcher error:`, error.message);
|
|
374
421
|
});
|
|
375
422
|
}
|
|
376
423
|
else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/importgen",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.27",
|
|
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": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"chokidar": "^4.0.3"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@types/node": "^25.
|
|
37
|
+
"@types/node": "^25.3.0"
|
|
38
38
|
},
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public"
|