@bobfrankston/importgen 0.1.23 → 0.1.25
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 +4 -1
- package/index.js +117 -4
- package/package.json +5 -3
|
@@ -6,7 +6,10 @@
|
|
|
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)",
|
|
12
|
+
"Bash(cd y:/dev/utils/importgen && npm run release 2>&1exit)"
|
|
10
13
|
]
|
|
11
14
|
}
|
|
12
15
|
}
|
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,14 +195,111 @@ 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')) {
|
|
194
283
|
console.log(`importgen ${packageJson.version}`);
|
|
195
284
|
process.exit(0);
|
|
196
285
|
}
|
|
286
|
+
const knownFlags = new Set(['-w', '--watch', '--freeze', '-freeze', '-v', '--version']);
|
|
287
|
+
// Report unrecognized flags as errors
|
|
288
|
+
const unknownArgs = args.filter(a => a.startsWith('-') && !knownFlags.has(a));
|
|
289
|
+
if (unknownArgs.length > 0) {
|
|
290
|
+
console.error(`[importgen] Error: unrecognized argument(s): ${unknownArgs.join(', ')}`);
|
|
291
|
+
console.error(` Usage: importgen [htmlfile] [-w|--watch] [--freeze] [-v|--version]`);
|
|
292
|
+
process.exit(1);
|
|
293
|
+
}
|
|
197
294
|
const watchMode = args.includes('-w') || args.includes('--watch');
|
|
198
|
-
const
|
|
295
|
+
const freezeMode = args.includes('--freeze') || args.includes('-freeze');
|
|
296
|
+
const positionalArgs = args.filter(a => !a.startsWith('-'));
|
|
297
|
+
if (positionalArgs.length > 1) {
|
|
298
|
+
console.error(`[importgen] Error: too many arguments: ${positionalArgs.join(', ')}`);
|
|
299
|
+
console.error(` Usage: importgen [htmlfile] [-w|--watch] [--freeze] [-v|--version]`);
|
|
300
|
+
process.exit(1);
|
|
301
|
+
}
|
|
302
|
+
const htmlArg = positionalArgs[0];
|
|
199
303
|
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
200
304
|
// Resolve HTML file: explicit argument, or search for common names
|
|
201
305
|
const possibleHtmlFiles = ['index.html', 'default.html', 'default.htm'];
|
|
@@ -206,6 +310,10 @@ if (!fs.existsSync(packageJsonPath)) {
|
|
|
206
310
|
console.error('[generate-importmap] Error: package.json not found in current directory');
|
|
207
311
|
process.exit(1);
|
|
208
312
|
}
|
|
313
|
+
if (freezeMode) {
|
|
314
|
+
freezeDependencies(packageJsonPath);
|
|
315
|
+
process.exit(0);
|
|
316
|
+
}
|
|
209
317
|
if (!htmlFilePath || !fs.existsSync(htmlFilePath)) {
|
|
210
318
|
if (htmlArg) {
|
|
211
319
|
console.error(`[generate-importmap] Error: ${htmlArg} not found in current directory`);
|
|
@@ -240,9 +348,14 @@ if (watchMode) {
|
|
|
240
348
|
watchedDepDirs = newDirs;
|
|
241
349
|
}
|
|
242
350
|
syncWatchedDeps(result.depDirs);
|
|
351
|
+
let debounceTimer = null;
|
|
243
352
|
const onChange = () => {
|
|
244
|
-
|
|
245
|
-
|
|
353
|
+
if (debounceTimer)
|
|
354
|
+
clearTimeout(debounceTimer);
|
|
355
|
+
debounceTimer = setTimeout(() => {
|
|
356
|
+
result = generateImportMap(packageJsonPath, htmlFilePath);
|
|
357
|
+
syncWatchedDeps(result.depDirs);
|
|
358
|
+
}, 100);
|
|
246
359
|
};
|
|
247
360
|
watcher.on('change', onChange);
|
|
248
361
|
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.25",
|
|
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",
|