@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.
- package/.claude/settings.local.json +3 -1
- package/index.js +102 -3
- package/package.json +5 -3
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
|
-
|
|
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
|
-
|
|
245
|
-
|
|
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.
|
|
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": "
|
|
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",
|