@bobfrankston/importgen 0.1.28 → 0.1.30

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/README.md CHANGED
@@ -103,6 +103,25 @@ The browser sees `date-utils`, looks it up in the import map, and loads `./node_
103
103
 
104
104
  The key point: your TypeScript source files are the same whether they run in Node or the browser. `importgen` just provides the mapping the browser needs to find the packages.
105
105
 
106
+ ## Programmatic API
107
+
108
+ `importgen` can be called from another application (e.g., `npmglobalize`) instead of the CLI:
109
+
110
+ ```typescript
111
+ import { importgen } from "@bobfrankston/importgen";
112
+
113
+ // Generate import map for a specific directory
114
+ importgen("/path/to/my/app");
115
+
116
+ // With explicit HTML filename
117
+ importgen("/path/to/my/app", "default.htm");
118
+
119
+ // Uses process.cwd() if no directory given
120
+ importgen();
121
+ ```
122
+
123
+ The function throws on missing `package.json` or HTML file (instead of calling `process.exit`), so callers can handle errors. Returns a `GenerateResult` with resolved dependency directories.
124
+
106
125
  ## How Entry Points Are Resolved
107
126
 
108
127
  For each dependency, `importgen` reads that package's `package.json` and picks the entry point in this order:
package/index-new.js ADDED
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Generate ES Module import map from package.json dependencies
4
+ * Injects into HTML files for native browser module loading
5
+ */
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import chokidar from 'chokidar';
9
+ import packageJson from './package.json' with { type: 'json' };
10
+ /** Timestamp prefix for log messages */
11
+ function ts() {
12
+ return `[${new Date().toLocaleTimeString()}]`;
13
+ }
14
+ /**
15
+ * Resolve dependency path based on version specifier
16
+ */
17
+ function resolveDependencyPath(packageDir, depName, depVersion) {
18
+ if (depVersion.startsWith('file:')) {
19
+ // Handle file: protocol - resolve relative to package directory
20
+ const relativePath = depVersion.slice(5); // Remove 'file:' prefix
21
+ return path.resolve(packageDir, relativePath);
22
+ }
23
+ else {
24
+ // npm or workspace dependency - walk up looking for node_modules (monorepo support)
25
+ let dir = packageDir;
26
+ while (true) {
27
+ const candidate = path.join(dir, 'node_modules', depName);
28
+ if (fs.existsSync(candidate)) {
29
+ return candidate;
30
+ }
31
+ const parent = path.dirname(dir);
32
+ if (parent === dir)
33
+ break; // filesystem root
34
+ dir = parent;
35
+ }
36
+ return null;
37
+ }
38
+ }
39
+ /**
40
+ * Resolve entry point from package.json
41
+ */
42
+ function resolveEntryPoint(pkg, packageDir) {
43
+ // 1. Check exports field (modern)
44
+ if (pkg.exports) {
45
+ if (typeof pkg.exports === 'string') {
46
+ return pkg.exports;
47
+ }
48
+ else if (pkg.exports['.']) {
49
+ const dotExport = pkg.exports['.'];
50
+ if (typeof dotExport === 'string') {
51
+ return dotExport;
52
+ }
53
+ else if (dotExport.import) {
54
+ return dotExport.import;
55
+ }
56
+ else if (dotExport.default) {
57
+ return dotExport.default;
58
+ }
59
+ }
60
+ }
61
+ // 2. Check module field (ESM)
62
+ if (pkg.module) {
63
+ return pkg.module;
64
+ }
65
+ // 3. Fall back to main field
66
+ if (pkg.main) {
67
+ return pkg.main;
68
+ }
69
+ // 4. Default to index.js
70
+ return './index.js';
71
+ }
72
+ /**
73
+ * Recursively collect all dependencies, avoiding circular references.
74
+ * Import map paths are computed relative to the HTML file's directory.
75
+ */
76
+ function collectDependencies(packageJsonPath, visited, dependencies, htmlDir, warnings, depPaths) {
77
+ try {
78
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
79
+ const packageDir = path.dirname(packageJsonPath);
80
+ const deps = packageJson.dependencies || {};
81
+ const dotDeps = packageJson['.dependencies'] || {};
82
+ for (const [depName, depVersion] of Object.entries(deps)) {
83
+ // Skip if already processed (circular dependency protection)
84
+ if (visited.has(depName)) {
85
+ continue;
86
+ }
87
+ visited.add(depName);
88
+ // Check if .dependencies has an override path for this package
89
+ const overrideVersion = dotDeps[depName];
90
+ const versionToUse = overrideVersion || depVersion;
91
+ // Resolve dependency path (real filesystem location)
92
+ let depPath = resolveDependencyPath(packageDir, depName, versionToUse);
93
+ if (!depPath) {
94
+ const warning = `Could not follow path for ${depName} (${versionToUse})`;
95
+ console.warn(`${ts()} [generate-importmap] Warning: ${warning}`);
96
+ warnings.push(warning);
97
+ continue;
98
+ }
99
+ // Check for .dependency backup directory and use it if it exists
100
+ const dependencyBackupPath = depPath + '.dependency';
101
+ if (fs.existsSync(dependencyBackupPath)) {
102
+ const warning = `Using backup path ${dependencyBackupPath}`;
103
+ console.warn(`${ts()} [generate-importmap] Warning: ${warning}`);
104
+ warnings.push(warning);
105
+ depPath = dependencyBackupPath;
106
+ }
107
+ // Track resolved dependency directory for watch mode
108
+ if (depPaths) {
109
+ depPaths.add(depPath);
110
+ }
111
+ const depPackageJsonPath = path.join(depPath, 'package.json');
112
+ if (!fs.existsSync(depPackageJsonPath)) {
113
+ const warning = `Could not follow path for ${depName} - no package.json found at ${depPath}`;
114
+ console.warn(`${ts()} [generate-importmap] Warning: ${warning}`);
115
+ warnings.push(warning);
116
+ continue;
117
+ }
118
+ // Read dependency's package.json
119
+ const depPackageJson = JSON.parse(fs.readFileSync(depPackageJsonPath, 'utf-8'));
120
+ // Resolve entry point
121
+ const entryPoint = resolveEntryPoint(depPackageJson, depPath);
122
+ const entryFile = entryPoint.startsWith('./') ? entryPoint.slice(2) : entryPoint;
123
+ // Generate import path relative to HTML directory
124
+ const relPath = path.relative(htmlDir, depPath).split(path.sep).join('/');
125
+ const depPrefix = relPath.startsWith('.') ? relPath : `./${relPath}`;
126
+ dependencies.set(depName, `${depPrefix}/${entryFile}`);
127
+ // Recursively process this dependency's dependencies
128
+ collectDependencies(depPackageJsonPath, visited, dependencies, htmlDir, warnings, depPaths);
129
+ }
130
+ }
131
+ catch (e) {
132
+ console.error(`${ts()} [generate-importmap] Error processing ${packageJsonPath}:`, e.message);
133
+ throw e; // Propagate so callers can avoid overwriting HTML with empty map
134
+ }
135
+ }
136
+ function generateImportMap(packageJsonPath, htmlFilePath) {
137
+ const result = { depDirs: [] };
138
+ try {
139
+ const htmlDir = path.dirname(htmlFilePath);
140
+ const visited = new Set();
141
+ const dependencies = new Map();
142
+ const depPaths = new Set();
143
+ const warnings = [];
144
+ // Recursively collect all dependencies
145
+ try {
146
+ collectDependencies(packageJsonPath, visited, dependencies, htmlDir, warnings, depPaths);
147
+ }
148
+ catch {
149
+ // Root package.json unreadable (e.g., mid-write) — skip update to preserve existing HTML
150
+ return result;
151
+ }
152
+ result.depDirs = Array.from(depPaths);
153
+ // Convert Map to plain object for JSON
154
+ const imports = {};
155
+ for (const [name, importPath] of dependencies) {
156
+ imports[name] = importPath;
157
+ }
158
+ // Generate warning comments
159
+ let warningComments = '';
160
+ if (warnings.length > 0) {
161
+ warningComments = warnings.map(w => `<!-- importgen: ${w} -->`).join('\n ') + '\n ';
162
+ }
163
+ const timestamp = new Date().toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'long' });
164
+ const generatedComment = `<!-- Generated by importgen ${packageJson.version} on ${timestamp} -->`;
165
+ const importMapScript = `${warningComments}${generatedComment}\n <script type="importmap">
166
+ ${JSON.stringify({ imports }, null, 12)}
167
+ </script>`;
168
+ // Read HTML file
169
+ let html = fs.readFileSync(htmlFilePath, 'utf-8');
170
+ // Replace import map section (including importgen comments and generated-by timestamp)
171
+ const importMapRegex = /(<!--\s*importgen:.*?-->\s*)*(<!--\s*Generated by importgen.*?-->\s*)?<script type="importmap">[\s\S]*?<\/script>/;
172
+ if (importMapRegex.test(html)) {
173
+ html = html.replace(importMapRegex, importMapScript);
174
+ }
175
+ else {
176
+ // Insert before </head> if not found
177
+ html = html.replace('</head>', ` ${importMapScript}\n</head>`);
178
+ }
179
+ // Write back to HTML file
180
+ fs.writeFileSync(htmlFilePath, html, 'utf-8');
181
+ console.log(`${ts()} [generate-importmap] Updated import map`);
182
+ console.log(` Scanned ${visited.size} dependencies, generated ${dependencies.size} entries`);
183
+ if (dependencies.size > 0) {
184
+ console.log(' Packages:', Array.from(dependencies.keys()).join(', '));
185
+ }
186
+ }
187
+ catch (e) {
188
+ console.error(`${ts()} [generate-importmap] Error:`, e.message);
189
+ process.exit(1);
190
+ }
191
+ return result;
192
+ }
193
+ /**
194
+ * API: run importgen on a directory.
195
+ * @param appDir - directory containing package.json and HTML file (defaults to cwd)
196
+ * @param htmlFile - HTML filename to update (auto-detected if omitted)
197
+ * @returns GenerateResult with resolved dependency directories
198
+ */
199
+ export function importgen(appDir, htmlFile) {
200
+ const dir = appDir || process.cwd();
201
+ const pkgPath = path.join(dir, 'package.json');
202
+ if (!fs.existsSync(pkgPath)) {
203
+ throw new Error(`package.json not found in ${dir}`);
204
+ }
205
+ const possibleHtmlFiles = ['index.html', 'default.html', 'default.htm'];
206
+ const htmlName = htmlFile || possibleHtmlFiles.find(f => fs.existsSync(path.join(dir, f)));
207
+ const htmlPath = htmlName ? path.join(dir, htmlName) : null;
208
+ if (!htmlPath || !fs.existsSync(htmlPath)) {
209
+ throw new Error(htmlFile
210
+ ? `${htmlFile} not found in ${dir}`
211
+ : `No HTML file found in ${dir}. Looking for: ${possibleHtmlFiles.join(', ')}`);
212
+ }
213
+ return generateImportMap(pkgPath, htmlPath);
214
+ }
215
+ // CLI entry point
216
+ if (import.meta.main) {
217
+ const args = process.argv.slice(2);
218
+ if (args.includes('-v') || args.includes('--version')) {
219
+ console.log(`importgen ${packageJson.version}`);
220
+ process.exit(0);
221
+ }
222
+ const knownFlags = new Set(['-w', '--watch', '-v', '--version']);
223
+ const unknownArgs = args.filter(a => a.startsWith('-') && !knownFlags.has(a));
224
+ if (unknownArgs.length > 0) {
225
+ console.error(`${ts()} [importgen] Error: unrecognized argument(s): ${unknownArgs.join(', ')}`);
226
+ console.error(` Usage: importgen [htmlfile] [-w|--watch] [-v|--version]`);
227
+ process.exit(1);
228
+ }
229
+ const watchMode = args.includes('-w') || args.includes('--watch');
230
+ const positionalArgs = args.filter(a => !a.startsWith('-'));
231
+ if (positionalArgs.length > 1) {
232
+ console.error(`${ts()} [importgen] Error: too many arguments: ${positionalArgs.join(', ')}`);
233
+ console.error(` Usage: importgen [htmlfile] [-w|--watch] [-v|--version]`);
234
+ process.exit(1);
235
+ }
236
+ const htmlArg = positionalArgs[0];
237
+ const packageJsonPath = path.join(process.cwd(), 'package.json');
238
+ const possibleHtmlFiles = ['index.html', 'default.html', 'default.htm'];
239
+ const htmlFileName = htmlArg || possibleHtmlFiles.find(f => fs.existsSync(path.join(process.cwd(), f)));
240
+ const htmlFilePath = htmlFileName ? path.join(process.cwd(), htmlFileName) : null;
241
+ if (!fs.existsSync(packageJsonPath)) {
242
+ console.error(`${ts()} [generate-importmap] Error: package.json not found in current directory`);
243
+ process.exit(1);
244
+ }
245
+ if (!htmlFilePath || !fs.existsSync(htmlFilePath)) {
246
+ if (htmlArg) {
247
+ console.error(`${ts()} [generate-importmap] Error: ${htmlArg} not found in current directory`);
248
+ }
249
+ else {
250
+ console.error(`${ts()} [generate-importmap] Error: No HTML file found. Looking for: ${possibleHtmlFiles.join(', ')}`);
251
+ }
252
+ process.exit(1);
253
+ }
254
+ if (watchMode) {
255
+ let result = generateImportMap(packageJsonPath, htmlFilePath);
256
+ let watchedDepDirs = new Set();
257
+ const watcher = chokidar.watch([packageJsonPath], {
258
+ persistent: true,
259
+ ignoreInitial: true
260
+ });
261
+ function syncWatchedDeps(depDirs) {
262
+ const newDirs = new Set(depDirs);
263
+ for (const dir of watchedDepDirs) {
264
+ if (!newDirs.has(dir)) {
265
+ watcher.unwatch(dir);
266
+ }
267
+ }
268
+ for (const dir of newDirs) {
269
+ if (!watchedDepDirs.has(dir)) {
270
+ watcher.add(dir);
271
+ }
272
+ }
273
+ watchedDepDirs = newDirs;
274
+ }
275
+ syncWatchedDeps(result.depDirs);
276
+ let debounceTimer = null;
277
+ const onChange = () => {
278
+ if (debounceTimer)
279
+ clearTimeout(debounceTimer);
280
+ debounceTimer = setTimeout(() => {
281
+ result = generateImportMap(packageJsonPath, htmlFilePath);
282
+ syncWatchedDeps(result.depDirs);
283
+ }, 100);
284
+ };
285
+ watcher.on('change', onChange);
286
+ watcher.on('add', onChange);
287
+ watcher.on('unlink', onChange);
288
+ watcher.on('ready', () => {
289
+ const total = 1 + watchedDepDirs.size;
290
+ console.log(`${ts()} [generate-importmap] Watching ${total} directories for changes...`);
291
+ if (watchedDepDirs.size > 0) {
292
+ for (const dir of watchedDepDirs) {
293
+ console.log(` ${dir}`);
294
+ }
295
+ }
296
+ });
297
+ watcher.on('error', (error) => {
298
+ console.error(`${ts()} [generate-importmap] Watcher error:`, error.message);
299
+ });
300
+ }
301
+ else {
302
+ generateImportMap(packageJsonPath, htmlFilePath);
303
+ }
304
+ }
305
+ //# sourceMappingURL=index.js.map
package/index.d.ts CHANGED
@@ -3,5 +3,15 @@
3
3
  * Generate ES Module import map from package.json dependencies
4
4
  * Injects into HTML files for native browser module loading
5
5
  */
6
- export {};
6
+ /** Result from generateImportMap including paths for watch mode */
7
+ export interface GenerateResult {
8
+ depDirs: string[]; /** Resolved dependency directories */
9
+ }
10
+ /**
11
+ * API: run importgen on a directory.
12
+ * @param appDir - directory containing package.json and HTML file (defaults to cwd)
13
+ * @param htmlFile - HTML filename to update (auto-detected if omitted)
14
+ * @returns GenerateResult with resolved dependency directories
15
+ */
16
+ export declare function importgen(appDir?: string, htmlFile?: string): GenerateResult;
7
17
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -20,19 +20,18 @@ function resolveDependencyPath(packageDir, depName, depVersion) {
20
20
  const relativePath = depVersion.slice(5); // Remove 'file:' prefix
21
21
  return path.resolve(packageDir, relativePath);
22
22
  }
23
- else if (depVersion.startsWith('workspace:')) {
24
- // Workspace dependencies - look in node_modules
25
- const nodeModulesPath = path.join(packageDir, 'node_modules', depName);
26
- if (fs.existsSync(nodeModulesPath)) {
27
- return nodeModulesPath;
28
- }
29
- return null;
30
- }
31
23
  else {
32
- // npm version - look in node_modules
33
- const nodeModulesPath = path.join(packageDir, 'node_modules', depName);
34
- if (fs.existsSync(nodeModulesPath)) {
35
- return nodeModulesPath;
24
+ // npm or workspace dependency - walk up looking for node_modules (monorepo support)
25
+ let dir = packageDir;
26
+ while (true) {
27
+ const candidate = path.join(dir, 'node_modules', depName);
28
+ if (fs.existsSync(candidate)) {
29
+ return candidate;
30
+ }
31
+ const parent = path.dirname(dir);
32
+ if (parent === dir)
33
+ break; // filesystem root
34
+ dir = parent;
36
35
  }
37
36
  return null;
38
37
  }
@@ -122,14 +121,24 @@ function collectDependencies(packageJsonPath, visited, dependencies, htmlDir, no
122
121
  // Resolve entry point
123
122
  const entryPoint = resolveEntryPoint(depPackageJson, depPath);
124
123
  const entryFile = entryPoint.startsWith('./') ? entryPoint.slice(2) : entryPoint;
125
- // Generate import map path through node_modules (follows symlinks)
126
- // Check if hoisted to top-level node_modules first
127
- const topLevelDir = path.join(htmlDir, 'node_modules', depName);
128
- let depPrefix;
129
- if (fs.existsSync(topLevelDir)) {
130
- depPrefix = `./node_modules/${depName}`;
124
+ // Generate import map path through node_modules
125
+ // Walk up from htmlDir looking for node_modules/depName (handles monorepo hoisting)
126
+ let depPrefix = null;
127
+ let searchDir = htmlDir;
128
+ while (true) {
129
+ const candidate = path.join(searchDir, 'node_modules', depName);
130
+ if (fs.existsSync(candidate)) {
131
+ const rel = path.relative(htmlDir, candidate).split(path.sep).join('/');
132
+ depPrefix = rel.startsWith('.') ? rel : `./${rel}`;
133
+ break;
134
+ }
135
+ const parent = path.dirname(searchDir);
136
+ if (parent === searchDir)
137
+ break; // filesystem root
138
+ searchDir = parent;
131
139
  }
132
- else {
140
+ if (!depPrefix) {
141
+ // Fallback for deps not in any ancestor node_modules
133
142
  depPrefix = `${nodeModulesPrefix}/${depName}`;
134
143
  }
135
144
  dependencies.set(depName, `${depPrefix}/${entryFile}`);
@@ -199,98 +208,116 @@ function generateImportMap(packageJsonPath, htmlFilePath) {
199
208
  }
200
209
  return result;
201
210
  }
202
- // Parse CLI arguments
203
- const args = process.argv.slice(2);
204
- if (args.includes('-v') || args.includes('--version')) {
205
- console.log(`importgen ${packageJson.version}`);
206
- process.exit(0);
207
- }
208
- const knownFlags = new Set(['-w', '--watch', '-v', '--version']);
209
- // Report unrecognized flags as errors
210
- const unknownArgs = args.filter(a => a.startsWith('-') && !knownFlags.has(a));
211
- if (unknownArgs.length > 0) {
212
- console.error(`${ts()} [importgen] Error: unrecognized argument(s): ${unknownArgs.join(', ')}`);
213
- console.error(` Usage: importgen [htmlfile] [-w|--watch] [-v|--version]`);
214
- process.exit(1);
215
- }
216
- const watchMode = args.includes('-w') || args.includes('--watch');
217
- const positionalArgs = args.filter(a => !a.startsWith('-'));
218
- if (positionalArgs.length > 1) {
219
- console.error(`${ts()} [importgen] Error: too many arguments: ${positionalArgs.join(', ')}`);
220
- console.error(` Usage: importgen [htmlfile] [-w|--watch] [-v|--version]`);
221
- process.exit(1);
222
- }
223
- const htmlArg = positionalArgs[0];
224
- const packageJsonPath = path.join(process.cwd(), 'package.json');
225
- // Resolve HTML file: explicit argument, or search for common names
226
- const possibleHtmlFiles = ['index.html', 'default.html', 'default.htm'];
227
- const htmlFileName = htmlArg || possibleHtmlFiles.find(f => fs.existsSync(path.join(process.cwd(), f)));
228
- const htmlFilePath = htmlFileName ? path.join(process.cwd(), htmlFileName) : null;
229
- // Check if files exist
230
- if (!fs.existsSync(packageJsonPath)) {
231
- console.error(`${ts()} [generate-importmap] Error: package.json not found in current directory`);
232
- process.exit(1);
233
- }
234
- if (!htmlFilePath || !fs.existsSync(htmlFilePath)) {
235
- if (htmlArg) {
236
- console.error(`${ts()} [generate-importmap] Error: ${htmlArg} not found in current directory`);
211
+ /**
212
+ * API: run importgen on a directory.
213
+ * @param appDir - directory containing package.json and HTML file (defaults to cwd)
214
+ * @param htmlFile - HTML filename to update (auto-detected if omitted)
215
+ * @returns GenerateResult with resolved dependency directories
216
+ */
217
+ export function importgen(appDir, htmlFile) {
218
+ const dir = appDir || process.cwd();
219
+ const pkgPath = path.join(dir, 'package.json');
220
+ if (!fs.existsSync(pkgPath)) {
221
+ throw new Error(`package.json not found in ${dir}`);
237
222
  }
238
- else {
239
- console.error(`${ts()} [generate-importmap] Error: No HTML file found. Looking for: ${possibleHtmlFiles.join(', ')}`);
223
+ const possibleHtmlFiles = ['index.html', 'default.html', 'default.htm'];
224
+ const htmlName = htmlFile || possibleHtmlFiles.find(f => fs.existsSync(path.join(dir, f)));
225
+ const htmlPath = htmlName ? path.join(dir, htmlName) : null;
226
+ if (!htmlPath || !fs.existsSync(htmlPath)) {
227
+ throw new Error(htmlFile
228
+ ? `${htmlFile} not found in ${dir}`
229
+ : `No HTML file found in ${dir}. Looking for: ${possibleHtmlFiles.join(', ')}`);
240
230
  }
241
- process.exit(1);
231
+ return generateImportMap(pkgPath, htmlPath);
242
232
  }
243
- if (watchMode) {
244
- let result = generateImportMap(packageJsonPath, htmlFilePath);
245
- let watchedDepDirs = new Set();
246
- const watcher = chokidar.watch([packageJsonPath], {
247
- persistent: true,
248
- ignoreInitial: true
249
- });
250
- /** Update watcher to include/remove dependency directories */
251
- function syncWatchedDeps(depDirs) {
252
- const newDirs = new Set(depDirs);
253
- // Remove dirs no longer in dependency list
254
- for (const dir of watchedDepDirs) {
255
- if (!newDirs.has(dir)) {
256
- watcher.unwatch(dir);
257
- }
233
+ // CLI entry point
234
+ if (import.meta.main) {
235
+ const args = process.argv.slice(2);
236
+ if (args.includes('-v') || args.includes('--version')) {
237
+ console.log(`importgen ${packageJson.version}`);
238
+ process.exit(0);
239
+ }
240
+ const knownFlags = new Set(['-w', '--watch', '-v', '--version']);
241
+ const unknownArgs = args.filter(a => a.startsWith('-') && !knownFlags.has(a));
242
+ if (unknownArgs.length > 0) {
243
+ console.error(`${ts()} [importgen] Error: unrecognized argument(s): ${unknownArgs.join(', ')}`);
244
+ console.error(` Usage: importgen [htmlfile] [-w|--watch] [-v|--version]`);
245
+ process.exit(1);
246
+ }
247
+ const watchMode = args.includes('-w') || args.includes('--watch');
248
+ const positionalArgs = args.filter(a => !a.startsWith('-'));
249
+ if (positionalArgs.length > 1) {
250
+ console.error(`${ts()} [importgen] Error: too many arguments: ${positionalArgs.join(', ')}`);
251
+ console.error(` Usage: importgen [htmlfile] [-w|--watch] [-v|--version]`);
252
+ process.exit(1);
253
+ }
254
+ const htmlArg = positionalArgs[0];
255
+ const packageJsonPath = path.join(process.cwd(), 'package.json');
256
+ const possibleHtmlFiles = ['index.html', 'default.html', 'default.htm'];
257
+ const htmlFileName = htmlArg || possibleHtmlFiles.find(f => fs.existsSync(path.join(process.cwd(), f)));
258
+ const htmlFilePath = htmlFileName ? path.join(process.cwd(), htmlFileName) : null;
259
+ if (!fs.existsSync(packageJsonPath)) {
260
+ console.error(`${ts()} [generate-importmap] Error: package.json not found in current directory`);
261
+ process.exit(1);
262
+ }
263
+ if (!htmlFilePath || !fs.existsSync(htmlFilePath)) {
264
+ if (htmlArg) {
265
+ console.error(`${ts()} [generate-importmap] Error: ${htmlArg} not found in current directory`);
258
266
  }
259
- // Add new dirs
260
- for (const dir of newDirs) {
261
- if (!watchedDepDirs.has(dir)) {
262
- watcher.add(dir);
263
- }
267
+ else {
268
+ console.error(`${ts()} [generate-importmap] Error: No HTML file found. Looking for: ${possibleHtmlFiles.join(', ')}`);
264
269
  }
265
- watchedDepDirs = newDirs;
270
+ process.exit(1);
266
271
  }
267
- syncWatchedDeps(result.depDirs);
268
- let debounceTimer = null;
269
- const onChange = () => {
270
- if (debounceTimer)
271
- clearTimeout(debounceTimer);
272
- debounceTimer = setTimeout(() => {
273
- result = generateImportMap(packageJsonPath, htmlFilePath);
274
- syncWatchedDeps(result.depDirs);
275
- }, 100);
276
- };
277
- watcher.on('change', onChange);
278
- watcher.on('add', onChange);
279
- watcher.on('unlink', onChange);
280
- watcher.on('ready', () => {
281
- const total = 1 + watchedDepDirs.size;
282
- console.log(`${ts()} [generate-importmap] Watching ${total} directories for changes...`);
283
- if (watchedDepDirs.size > 0) {
272
+ if (watchMode) {
273
+ let result = generateImportMap(packageJsonPath, htmlFilePath);
274
+ let watchedDepDirs = new Set();
275
+ const watcher = chokidar.watch([packageJsonPath], {
276
+ persistent: true,
277
+ ignoreInitial: true
278
+ });
279
+ function syncWatchedDeps(depDirs) {
280
+ const newDirs = new Set(depDirs);
284
281
  for (const dir of watchedDepDirs) {
285
- console.log(` ${dir}`);
282
+ if (!newDirs.has(dir)) {
283
+ watcher.unwatch(dir);
284
+ }
285
+ }
286
+ for (const dir of newDirs) {
287
+ if (!watchedDepDirs.has(dir)) {
288
+ watcher.add(dir);
289
+ }
286
290
  }
291
+ watchedDepDirs = newDirs;
287
292
  }
288
- });
289
- watcher.on('error', (error) => {
290
- console.error(`${ts()} [generate-importmap] Watcher error:`, error.message);
291
- });
292
- }
293
- else {
294
- generateImportMap(packageJsonPath, htmlFilePath);
293
+ syncWatchedDeps(result.depDirs);
294
+ let debounceTimer = null;
295
+ const onChange = () => {
296
+ if (debounceTimer)
297
+ clearTimeout(debounceTimer);
298
+ debounceTimer = setTimeout(() => {
299
+ result = generateImportMap(packageJsonPath, htmlFilePath);
300
+ syncWatchedDeps(result.depDirs);
301
+ }, 100);
302
+ };
303
+ watcher.on('change', onChange);
304
+ watcher.on('add', onChange);
305
+ watcher.on('unlink', onChange);
306
+ watcher.on('ready', () => {
307
+ const total = 1 + watchedDepDirs.size;
308
+ console.log(`${ts()} [generate-importmap] Watching ${total} directories for changes...`);
309
+ if (watchedDepDirs.size > 0) {
310
+ for (const dir of watchedDepDirs) {
311
+ console.log(` ${dir}`);
312
+ }
313
+ }
314
+ });
315
+ watcher.on('error', (error) => {
316
+ console.error(`${ts()} [generate-importmap] Watcher error:`, error.message);
317
+ });
318
+ }
319
+ else {
320
+ generateImportMap(packageJsonPath, htmlFilePath);
321
+ }
295
322
  }
296
323
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "@bobfrankston/importgen",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "description": "Generate ES Module import maps from package.json dependencies for native browser module loading",
5
5
  "main": "index.js",
6
+ "exports": {
7
+ ".": "./index.js"
8
+ },
6
9
  "bin": {
7
10
  "importgen": "index.js"
8
11
  },
9
12
  "type": "module",
10
13
  "scripts": {
14
+ "start": "node index.js --watch",
11
15
  "build": "tsc",
12
16
  "watch": "tsc -w",
13
17
  "preversion": "npm run build",
@@ -1,17 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(npm run build:*)",
5
- "Bash(npmglobalize)",
6
- "Bash(npx tsc)",
7
- "Bash(node index.js:*)",
8
- "Bash(dir:*)",
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)",
13
- "Bash(cd y:/dev/utils/importgen && npx tsc --noEmit 2>&1)",
14
- "Bash(cd:*)"
15
- ]
16
- }
17
- }