@bobfrankston/importgen 0.1.26 → 0.1.28
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/index.js +20 -146
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import fs from 'node:fs';
|
|
7
7
|
import path from 'node:path';
|
|
8
|
-
import { execSync } from 'node:child_process';
|
|
9
8
|
import chokidar from 'chokidar';
|
|
10
9
|
import packageJson from './package.json' with { type: 'json' };
|
|
10
|
+
/** Timestamp prefix for log messages */
|
|
11
|
+
function ts() {
|
|
12
|
+
return `[${new Date().toLocaleTimeString()}]`;
|
|
13
|
+
}
|
|
11
14
|
/**
|
|
12
15
|
* Resolve dependency path based on version specifier
|
|
13
16
|
*/
|
|
@@ -91,7 +94,7 @@ function collectDependencies(packageJsonPath, visited, dependencies, htmlDir, no
|
|
|
91
94
|
let depPath = resolveDependencyPath(packageDir, depName, versionToUse);
|
|
92
95
|
if (!depPath) {
|
|
93
96
|
const warning = `Could not follow path for ${depName} (${versionToUse})`;
|
|
94
|
-
console.warn(
|
|
97
|
+
console.warn(`${ts()} [generate-importmap] Warning: ${warning}`);
|
|
95
98
|
warnings.push(warning);
|
|
96
99
|
continue;
|
|
97
100
|
}
|
|
@@ -99,7 +102,7 @@ function collectDependencies(packageJsonPath, visited, dependencies, htmlDir, no
|
|
|
99
102
|
const dependencyBackupPath = depPath + '.dependency';
|
|
100
103
|
if (fs.existsSync(dependencyBackupPath)) {
|
|
101
104
|
const warning = `Using backup path ${dependencyBackupPath}`;
|
|
102
|
-
console.warn(
|
|
105
|
+
console.warn(`${ts()} [generate-importmap] Warning: ${warning}`);
|
|
103
106
|
warnings.push(warning);
|
|
104
107
|
depPath = dependencyBackupPath;
|
|
105
108
|
}
|
|
@@ -110,7 +113,7 @@ function collectDependencies(packageJsonPath, visited, dependencies, htmlDir, no
|
|
|
110
113
|
const depPackageJsonPath = path.join(depPath, 'package.json');
|
|
111
114
|
if (!fs.existsSync(depPackageJsonPath)) {
|
|
112
115
|
const warning = `Could not follow path for ${depName} - no package.json found at ${depPath}`;
|
|
113
|
-
console.warn(
|
|
116
|
+
console.warn(`${ts()} [generate-importmap] Warning: ${warning}`);
|
|
114
117
|
warnings.push(warning);
|
|
115
118
|
continue;
|
|
116
119
|
}
|
|
@@ -135,7 +138,7 @@ function collectDependencies(packageJsonPath, visited, dependencies, htmlDir, no
|
|
|
135
138
|
}
|
|
136
139
|
}
|
|
137
140
|
catch (e) {
|
|
138
|
-
console.error(
|
|
141
|
+
console.error(`${ts()} [generate-importmap] Error processing ${packageJsonPath}:`, e.message);
|
|
139
142
|
throw e; // Propagate so callers can avoid overwriting HTML with empty map
|
|
140
143
|
}
|
|
141
144
|
}
|
|
@@ -184,158 +187,37 @@ function generateImportMap(packageJsonPath, htmlFilePath) {
|
|
|
184
187
|
}
|
|
185
188
|
// Write back to HTML file
|
|
186
189
|
fs.writeFileSync(htmlFilePath, html, 'utf-8');
|
|
187
|
-
console.log(
|
|
190
|
+
console.log(`${ts()} [generate-importmap] Updated import map`);
|
|
188
191
|
console.log(` Scanned ${visited.size} dependencies, generated ${dependencies.size} entries`);
|
|
189
192
|
if (dependencies.size > 0) {
|
|
190
193
|
console.log(' Packages:', Array.from(dependencies.keys()).join(', '));
|
|
191
194
|
}
|
|
192
195
|
}
|
|
193
196
|
catch (e) {
|
|
194
|
-
console.error(
|
|
197
|
+
console.error(`${ts()} [generate-importmap] Error:`, e.message);
|
|
195
198
|
process.exit(1);
|
|
196
199
|
}
|
|
197
200
|
return result;
|
|
198
201
|
}
|
|
199
|
-
/**
|
|
200
|
-
* Freeze dependencies: replace junctions/symlinks in node_modules with real copies.
|
|
201
|
-
* Also adds a preinstall guard to package.json to prevent npm install from undoing the freeze.
|
|
202
|
-
*/
|
|
203
|
-
function freezeDependencies(packageJsonPath) {
|
|
204
|
-
const nodeModulesDir = path.join(process.cwd(), 'node_modules');
|
|
205
|
-
if (!fs.existsSync(nodeModulesDir)) {
|
|
206
|
-
console.error('[importgen] Error: node_modules not found');
|
|
207
|
-
process.exit(1);
|
|
208
|
-
}
|
|
209
|
-
/** Check if a directory entry is a junction or symlink by comparing realpath to its path */
|
|
210
|
-
function isLinked(entryPath) {
|
|
211
|
-
try {
|
|
212
|
-
const stat = fs.lstatSync(entryPath);
|
|
213
|
-
// Symlink check (works on Linux/Mac symlinks)
|
|
214
|
-
if (stat.isSymbolicLink())
|
|
215
|
-
return true;
|
|
216
|
-
// Junction check (Windows): realpath differs from the entry path
|
|
217
|
-
if (stat.isDirectory()) {
|
|
218
|
-
const real = fs.realpathSync(entryPath);
|
|
219
|
-
return path.resolve(real) !== path.resolve(entryPath);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
catch (e) {
|
|
223
|
-
console.error(`[importgen] Error checking ${entryPath}: ${e.message}`);
|
|
224
|
-
}
|
|
225
|
-
return false;
|
|
226
|
-
}
|
|
227
|
-
/** Freeze a single entry if it's a junction/symlink */
|
|
228
|
-
function freezeEntry(entryPath, displayName) {
|
|
229
|
-
if (!isLinked(entryPath))
|
|
230
|
-
return;
|
|
231
|
-
const target = fs.realpathSync(entryPath);
|
|
232
|
-
console.log(` Freezing ${displayName} (${target})`);
|
|
233
|
-
fs.rmSync(entryPath, { recursive: true });
|
|
234
|
-
fs.cpSync(target, entryPath, { recursive: true });
|
|
235
|
-
}
|
|
236
|
-
console.log('[importgen] Freezing dependencies...');
|
|
237
|
-
let frozenCount = 0;
|
|
238
|
-
const entries = fs.readdirSync(nodeModulesDir);
|
|
239
|
-
for (const entry of entries) {
|
|
240
|
-
const entryPath = path.join(nodeModulesDir, entry);
|
|
241
|
-
if (entry.startsWith('@')) {
|
|
242
|
-
// Scoped package — walk one level deeper
|
|
243
|
-
if (!fs.statSync(entryPath).isDirectory())
|
|
244
|
-
continue;
|
|
245
|
-
const scopedEntries = fs.readdirSync(entryPath);
|
|
246
|
-
for (const scoped of scopedEntries) {
|
|
247
|
-
const scopedPath = path.join(entryPath, scoped);
|
|
248
|
-
const before = isLinked(scopedPath);
|
|
249
|
-
freezeEntry(scopedPath, `${entry}/${scoped}`);
|
|
250
|
-
if (before)
|
|
251
|
-
frozenCount++;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
const before = isLinked(entryPath);
|
|
256
|
-
freezeEntry(entryPath, entry);
|
|
257
|
-
if (before)
|
|
258
|
-
frozenCount++;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
console.log(`[importgen] Frozen ${frozenCount} linked packages`);
|
|
262
|
-
// Add preinstall guard to package.json
|
|
263
|
-
try {
|
|
264
|
-
const pkgRaw = fs.readFileSync(packageJsonPath, 'utf-8');
|
|
265
|
-
const pkg = JSON.parse(pkgRaw);
|
|
266
|
-
if (!pkg.scripts)
|
|
267
|
-
pkg.scripts = {};
|
|
268
|
-
if (pkg.scripts.preinstall) {
|
|
269
|
-
// Rename existing preinstall so unfreeze can restore it
|
|
270
|
-
pkg.scripts['frozen-preinstall'] = pkg.scripts.preinstall;
|
|
271
|
-
console.log(`[importgen] Saved existing preinstall script as "frozen-preinstall"`);
|
|
272
|
-
}
|
|
273
|
-
pkg.scripts.preinstall = 'echo FROZEN: dependencies were frozen by importgen. Use importgen --unfreeze to restore. && exit 1';
|
|
274
|
-
fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
275
|
-
console.log('[importgen] Added preinstall guard to package.json');
|
|
276
|
-
}
|
|
277
|
-
catch (e) {
|
|
278
|
-
console.error(`[importgen] Error updating package.json: ${e.message}`);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Unfreeze: remove preinstall guard, restore original preinstall if saved, then run npm install.
|
|
283
|
-
*/
|
|
284
|
-
function unfreezeDependencies(packageJsonPath) {
|
|
285
|
-
try {
|
|
286
|
-
const pkgRaw = fs.readFileSync(packageJsonPath, 'utf-8');
|
|
287
|
-
const pkg = JSON.parse(pkgRaw);
|
|
288
|
-
if (!pkg.scripts?.preinstall) {
|
|
289
|
-
console.log('[importgen] No preinstall guard found — nothing to unfreeze');
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
if (pkg.scripts['frozen-preinstall']) {
|
|
293
|
-
pkg.scripts.preinstall = pkg.scripts['frozen-preinstall'];
|
|
294
|
-
delete pkg.scripts['frozen-preinstall'];
|
|
295
|
-
console.log('[importgen] Restored original preinstall script');
|
|
296
|
-
}
|
|
297
|
-
else {
|
|
298
|
-
delete pkg.scripts.preinstall;
|
|
299
|
-
console.log('[importgen] Removed preinstall guard');
|
|
300
|
-
}
|
|
301
|
-
fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
302
|
-
}
|
|
303
|
-
catch (e) {
|
|
304
|
-
console.error(`[importgen] Error updating package.json: ${e.message}`);
|
|
305
|
-
process.exit(1);
|
|
306
|
-
}
|
|
307
|
-
// Run npm install to restore proper dependencies
|
|
308
|
-
console.log('[importgen] Running npm install...');
|
|
309
|
-
try {
|
|
310
|
-
execSync('npm install', { stdio: 'inherit', cwd: process.cwd() });
|
|
311
|
-
console.log('[importgen] Unfreeze complete');
|
|
312
|
-
}
|
|
313
|
-
catch (e) {
|
|
314
|
-
console.error(`[importgen] npm install failed: ${e.message}`);
|
|
315
|
-
process.exit(1);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
202
|
// Parse CLI arguments
|
|
319
203
|
const args = process.argv.slice(2);
|
|
320
204
|
if (args.includes('-v') || args.includes('--version')) {
|
|
321
205
|
console.log(`importgen ${packageJson.version}`);
|
|
322
206
|
process.exit(0);
|
|
323
207
|
}
|
|
324
|
-
const knownFlags = new Set(['-w', '--watch', '
|
|
208
|
+
const knownFlags = new Set(['-w', '--watch', '-v', '--version']);
|
|
325
209
|
// Report unrecognized flags as errors
|
|
326
210
|
const unknownArgs = args.filter(a => a.startsWith('-') && !knownFlags.has(a));
|
|
327
211
|
if (unknownArgs.length > 0) {
|
|
328
|
-
console.error(
|
|
329
|
-
console.error(` Usage: importgen [htmlfile] [-w|--watch] [
|
|
212
|
+
console.error(`${ts()} [importgen] Error: unrecognized argument(s): ${unknownArgs.join(', ')}`);
|
|
213
|
+
console.error(` Usage: importgen [htmlfile] [-w|--watch] [-v|--version]`);
|
|
330
214
|
process.exit(1);
|
|
331
215
|
}
|
|
332
216
|
const watchMode = args.includes('-w') || args.includes('--watch');
|
|
333
|
-
const freezeMode = args.includes('--freeze') || args.includes('-freeze');
|
|
334
|
-
const unfreezeMode = args.includes('--unfreeze') || args.includes('-unfreeze');
|
|
335
217
|
const positionalArgs = args.filter(a => !a.startsWith('-'));
|
|
336
218
|
if (positionalArgs.length > 1) {
|
|
337
|
-
console.error(
|
|
338
|
-
console.error(` Usage: importgen [htmlfile] [-w|--watch] [
|
|
219
|
+
console.error(`${ts()} [importgen] Error: too many arguments: ${positionalArgs.join(', ')}`);
|
|
220
|
+
console.error(` Usage: importgen [htmlfile] [-w|--watch] [-v|--version]`);
|
|
339
221
|
process.exit(1);
|
|
340
222
|
}
|
|
341
223
|
const htmlArg = positionalArgs[0];
|
|
@@ -346,23 +228,15 @@ const htmlFileName = htmlArg || possibleHtmlFiles.find(f => fs.existsSync(path.j
|
|
|
346
228
|
const htmlFilePath = htmlFileName ? path.join(process.cwd(), htmlFileName) : null;
|
|
347
229
|
// Check if files exist
|
|
348
230
|
if (!fs.existsSync(packageJsonPath)) {
|
|
349
|
-
console.error(
|
|
231
|
+
console.error(`${ts()} [generate-importmap] Error: package.json not found in current directory`);
|
|
350
232
|
process.exit(1);
|
|
351
233
|
}
|
|
352
|
-
if (freezeMode) {
|
|
353
|
-
freezeDependencies(packageJsonPath);
|
|
354
|
-
process.exit(0);
|
|
355
|
-
}
|
|
356
|
-
if (unfreezeMode) {
|
|
357
|
-
unfreezeDependencies(packageJsonPath);
|
|
358
|
-
process.exit(0);
|
|
359
|
-
}
|
|
360
234
|
if (!htmlFilePath || !fs.existsSync(htmlFilePath)) {
|
|
361
235
|
if (htmlArg) {
|
|
362
|
-
console.error(
|
|
236
|
+
console.error(`${ts()} [generate-importmap] Error: ${htmlArg} not found in current directory`);
|
|
363
237
|
}
|
|
364
238
|
else {
|
|
365
|
-
console.error(
|
|
239
|
+
console.error(`${ts()} [generate-importmap] Error: No HTML file found. Looking for: ${possibleHtmlFiles.join(', ')}`);
|
|
366
240
|
}
|
|
367
241
|
process.exit(1);
|
|
368
242
|
}
|
|
@@ -405,7 +279,7 @@ if (watchMode) {
|
|
|
405
279
|
watcher.on('unlink', onChange);
|
|
406
280
|
watcher.on('ready', () => {
|
|
407
281
|
const total = 1 + watchedDepDirs.size;
|
|
408
|
-
console.log(
|
|
282
|
+
console.log(`${ts()} [generate-importmap] Watching ${total} directories for changes...`);
|
|
409
283
|
if (watchedDepDirs.size > 0) {
|
|
410
284
|
for (const dir of watchedDepDirs) {
|
|
411
285
|
console.log(` ${dir}`);
|
|
@@ -413,7 +287,7 @@ if (watchMode) {
|
|
|
413
287
|
}
|
|
414
288
|
});
|
|
415
289
|
watcher.on('error', (error) => {
|
|
416
|
-
console.error(
|
|
290
|
+
console.error(`${ts()} [generate-importmap] Watcher error:`, error.message);
|
|
417
291
|
});
|
|
418
292
|
}
|
|
419
293
|
else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/importgen",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.28",
|
|
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"
|