@bobfrankston/importgen 0.1.13 → 0.1.15

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.
@@ -3,7 +3,8 @@
3
3
  "allow": [
4
4
  "Bash(npm run build:*)",
5
5
  "Bash(npmglobalize)",
6
- "Bash(npx tsc)"
6
+ "Bash(npx tsc)",
7
+ "Bash(node index.js:*)"
7
8
  ]
8
9
  }
9
10
  }
package/README.md CHANGED
@@ -1,8 +1,20 @@
1
1
  # @bobfrankston/importgen
2
2
 
3
- Generate ES Module import maps from package.json dependencies for native browser module loading.
3
+ Use the same npm packages and `import` statements in the browser as in Node.js/Bun -- no bundler required.
4
4
 
5
- ## Installation
5
+ ## Why
6
+
7
+ In Node.js you write:
8
+
9
+ ```typescript
10
+ import { formatDate } from "date-utils";
11
+ ```
12
+
13
+ Node resolves `date-utils` by looking in `node_modules`. Browsers can't do that. Normally you'd need a bundler (webpack, rollup, etc.) to make bare module specifiers work.
14
+
15
+ Browsers now support [import maps](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) -- a `<script type="importmap">` block that maps package names to file paths. `importgen` reads your `package.json` and generates this import map automatically, injecting it into your HTML file. Your code stays identical between Node and browser -- transparent, no changes needed.
16
+
17
+ ## Install
6
18
 
7
19
  ```bash
8
20
  npm install -g @bobfrankston/importgen
@@ -10,82 +22,172 @@ npm install -g @bobfrankston/importgen
10
22
 
11
23
  ## Usage
12
24
 
13
- In a directory with `package.json` and `index.html`:
25
+ Run from your project directory (containing `package.json` and your HTML file):
14
26
 
15
27
  ```bash
16
- # Generate once
28
+ importgen [htmlfile] [--watch|-w]
29
+ ```
30
+
31
+ ```bash
32
+ # Generate import map in index.html (default)
17
33
  importgen
18
34
 
19
- # Watch for package.json changes
20
- importgen -w
35
+ # Specify a different HTML file
36
+ importgen default.htm
37
+ importgen app.html
38
+
39
+ # Watch mode: regenerate whenever package.json changes
40
+ importgen --watch
41
+ importgen default.htm --watch
21
42
  ```
22
43
 
23
- ## Automatic Generation with VS Code Tasks
44
+ `importgen` reads dependencies from `package.json` in the current directory and injects the import map into the HTML file.
45
+
46
+ If no HTML file is specified, it searches for `index.html`, `default.html`, `default.htm` (in that order) and uses the first one found.
47
+
48
+ If the HTML already has a `<script type="importmap">` block, it is replaced in place. Otherwise the block is inserted before `</head>`.
24
49
 
25
- Add to `.vscode/tasks.json`:
50
+ ## What It Generates
51
+
52
+ Given a `package.json` like:
26
53
 
27
54
  ```json
28
55
  {
29
- "version": "2.0.0",
30
- "tasks": [
31
- {
32
- "label": "importgen: watch",
33
- "type": "shell",
34
- "command": "importgen",
35
- "args": ["-w"],
36
- "runOptions": {
37
- "runOn": "folderOpen"
38
- },
39
- "isBackground": true
56
+ "dependencies": {
57
+ "date-utils": "^2.0.0",
58
+ "my-ui-lib": "file:../shared/my-ui-lib"
59
+ }
60
+ }
61
+ ```
62
+
63
+ `importgen` produces:
64
+
65
+ ```html
66
+ <script type="importmap">
67
+ {
68
+ "imports": {
69
+ "date-utils": "./node_modules/date-utils/index.js",
70
+ "my-ui-lib": "./node_modules/my-ui-lib/index.js",
71
+ "color-convert": "./node_modules/color-convert/index.js"
40
72
  }
41
- ]
42
73
  }
74
+ </script>
75
+ ```
76
+
77
+ Transitive dependencies are included automatically -- if `my-ui-lib` depends on `color-convert`, it appears in the map too. Circular dependencies are detected and handled.
78
+
79
+ ## Using Imports in the Browser
80
+
81
+ Once the import map is in your HTML, `<script type="module">` code uses the same imports as Node:
82
+
83
+ ```typescript
84
+ // This works identically in Node AND the browser
85
+ import { formatDate } from "date-utils";
86
+ import { render } from "my-ui-lib";
87
+ import { initApp } from "./app.js";
43
88
  ```
44
89
 
45
- ## How It Works
90
+ The browser sees `date-utils`, looks it up in the import map, and loads `./node_modules/date-utils/index.js`. Local relative imports (`./app.js`) work as always without the import map.
46
91
 
47
- 1. Reads `package.json` dependencies
48
- 2. **Recursively resolves** all transitive dependencies
49
- 3. Handles `file:` protocol dependencies and npm packages
50
- 4. Detects and avoids circular dependencies
51
- 5. Resolves correct entry points using `exports`, `module`, or `main` fields
52
- 6. Generates import map with relative paths from HTML location
53
- 7. Injects or updates `<script type="importmap">` in `index.html`
54
- 8. In watch mode, regenerates when `package.json` changes
92
+ 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.
55
93
 
56
- ### Circular Dependency Protection
94
+ ## How Entry Points Are Resolved
57
95
 
58
- The tool tracks visited packages to prevent infinite loops when packages have circular dependencies (A → B → A). Each package is processed only once.
96
+ For each dependency, `importgen` reads that package's `package.json` and picks the entry point in this order:
59
97
 
60
- ## PWA Compatible
98
+ 1. `exports["."].import` or `exports["."].default` (modern ESM)
99
+ 2. `module` field (ESM convention)
100
+ 3. `main` field
101
+ 4. `./index.js` (fallback)
102
+
103
+ ## Dependency Types
61
104
 
62
- Generated import maps are static and can be cached by service workers, making this ideal for Progressive Web Apps.
105
+ - **npm versions** (`^1.2.3`) -- resolved from `node_modules/`
106
+ - **`file:` paths** (`file:../path/to/package`) -- resolved relative to `package.json`
107
+ - **`workspace:` references** -- resolved from `node_modules/`
63
108
 
64
- ## Example
109
+ ## `.dependencies` Override
110
+
111
+ Add a `.dependencies` field to `package.json` to override resolution paths for specific packages without changing the real `dependencies`:
65
112
 
66
- **package.json:**
67
113
  ```json
68
114
  {
69
115
  "dependencies": {
70
- "@bobfrankston/lxlan": "^0.1.0",
71
- "@bobfrankston/colorlib": "file:../colorlib"
116
+ "my-utils": "^1.0.0"
117
+ },
118
+ ".dependencies": {
119
+ "my-utils": "file:../local-my-utils"
72
120
  }
73
121
  }
74
122
  ```
75
123
 
76
- If `lxlan` depends on `lxlan-browser`, and `lxlan-browser` depends on `colorlib`, **all three packages** will be included in the generated import map:
124
+ The import map will use the `.dependencies` path instead of the `node_modules` path for that package.
77
125
 
78
- **Generated in index.html:**
79
- ```html
80
- <script type="importmap">
81
- {
82
- "imports": {
83
- "@bobfrankston/lxlan": "./node_modules/@bobfrankston/lxlan/index.js",
84
- "@bobfrankston/lxlan-browser": "./node_modules/@bobfrankston/lxlan-browser/index.js",
85
- "@bobfrankston/colorlib": "../colorlib/index.js"
126
+ ## VS Code tasks.json Integration
127
+
128
+ 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.
129
+
130
+ Example `.vscode/tasks.json`:
131
+
132
+ ```json
133
+ {
134
+ "version": "2.0.0",
135
+ "tasks": [
136
+ {
137
+ "label": "tsc: watch",
138
+ "type": "shell",
139
+ "command": "tsc",
140
+ "args": ["--watch"],
141
+ "runOptions": {
142
+ "runOn": "folderOpen"
143
+ },
144
+ "problemMatcher": "$tsc-watch",
145
+ "isBackground": true,
146
+ "group": {
147
+ "kind": "build",
148
+ "isDefault": true
149
+ }
150
+ },
151
+ {
152
+ "label": "importgen: watch",
153
+ "type": "shell",
154
+ "command": "importgen",
155
+ "args": ["--watch"],
156
+ "runOptions": {
157
+ "runOn": "folderOpen"
158
+ },
159
+ "isBackground": true,
160
+ "problemMatcher": {
161
+ "pattern": {
162
+ "regexp": "^$",
163
+ "file": 1,
164
+ "location": 2,
165
+ "message": 3
166
+ },
167
+ "background": {
168
+ "activeOnStart": true,
169
+ "beginsPattern": "^\\[generate-importmap\\].*watching.*$",
170
+ "endsPattern": "^\\[generate-importmap\\].*Updated.*$"
171
+ }
172
+ }
86
173
  }
87
- }
88
- </script>
174
+ ]
175
+ }
89
176
  ```
90
177
 
91
- Note: The tool automatically resolves transitive dependencies and handles both npm packages and `file:` protocol dependencies.
178
+ Key points:
179
+
180
+ - **`"runOn": "folderOpen"`** starts both tasks automatically when you open the workspace
181
+ - **`"isBackground": true`** keeps them running as persistent watchers
182
+ - The custom `problemMatcher` for importgen tells VS Code this is a long-running watcher, not a one-shot build
183
+ - If your HTML file isn't `index.html`, add it to the args: `"args": ["default.htm", "--watch"]`
184
+
185
+ Workflow: edit `package.json` (add/remove a dependency) -> `importgen` regenerates the import map in the HTML -> `tsc` recompiles TypeScript -> browser picks up changes on reload.
186
+
187
+ ## PWA Compatible
188
+
189
+ Generated import maps are static and can be cached by service workers.
190
+
191
+ ## License
192
+
193
+ MIT
package/index.js CHANGED
@@ -175,39 +175,24 @@ function generateImportMap(packageJsonPath, htmlFilePath) {
175
175
  // Parse CLI arguments
176
176
  const args = process.argv.slice(2);
177
177
  const watchMode = args.includes('-w') || args.includes('--watch');
178
+ const htmlArg = args.find(a => a !== '-w' && a !== '--watch');
178
179
  const packageJsonPath = path.join(process.cwd(), 'package.json');
179
- // Find HTML file - check command line arg first, then try common names
180
- function findHtmlFile() {
181
- // Check for explicit filename argument (non-flag argument)
182
- const htmlArg = args.find(arg => !arg.startsWith('-') && (arg.endsWith('.html') || arg.endsWith('.htm')));
183
- if (htmlArg) {
184
- const explicitPath = path.join(process.cwd(), htmlArg);
185
- if (fs.existsSync(explicitPath)) {
186
- return explicitPath;
187
- }
188
- console.error(`[generate-importmap] Error: ${htmlArg} not found in current directory`);
189
- process.exit(1);
190
- }
191
- // Try common HTML file names in order of preference
192
- const candidates = ['index.html', 'default.html', 'default.htm'];
193
- for (const candidate of candidates) {
194
- const candidatePath = path.join(process.cwd(), candidate);
195
- if (fs.existsSync(candidatePath)) {
196
- return candidatePath;
197
- }
198
- }
199
- return null;
200
- }
201
- const htmlFilePath = findHtmlFile();
180
+ // Resolve HTML file: explicit argument, or search for common names
181
+ const possibleHtmlFiles = ['index.html', 'default.html', 'default.htm'];
182
+ const htmlFileName = htmlArg || possibleHtmlFiles.find(f => fs.existsSync(path.join(process.cwd(), f)));
183
+ const htmlFilePath = htmlFileName ? path.join(process.cwd(), htmlFileName) : null;
202
184
  // Check if files exist
203
185
  if (!fs.existsSync(packageJsonPath)) {
204
186
  console.error('[generate-importmap] Error: package.json not found in current directory');
205
187
  process.exit(1);
206
188
  }
207
- if (!htmlFilePath) {
208
- console.error('[generate-importmap] Error: index.html not found in current directory');
209
- console.error(' Also checked: default.html, default.htm');
210
- console.error(' Use: importgen [filename.html] to specify a custom HTML file');
189
+ if (!htmlFilePath || !fs.existsSync(htmlFilePath)) {
190
+ if (htmlArg) {
191
+ console.error(`[generate-importmap] Error: ${htmlArg} not found in current directory`);
192
+ }
193
+ else {
194
+ console.error(`[generate-importmap] Error: No HTML file found. Looking for: ${possibleHtmlFiles.join(', ')}`);
195
+ }
211
196
  process.exit(1);
212
197
  }
213
198
  if (watchMode) {
package/package.json CHANGED
@@ -1,37 +1,40 @@
1
- {
2
- "name": "@bobfrankston/importgen",
3
- "version": "0.1.13",
4
- "description": "Generate ES Module import maps from package.json dependencies for native browser module loading",
5
- "main": "index.js",
6
- "bin": {
7
- "importgen": "index.js"
8
- },
9
- "type": "module",
10
- "scripts": {
11
- "build": "tsc",
12
- "watch": "tsc -w",
13
- "preversion": "npm run build",
14
- "postversion": "echo Version updated to $(node -p \"require('./package.json').version\")",
15
- "release": "git add -A && git diff-index --quiet HEAD || git commit -m 'Build for release' && npm version patch",
16
- "installer": "npm run release && npm install -g ."
17
- },
18
- "keywords": [
19
- "import-map",
20
- "esm",
21
- "browser",
22
- "modules",
23
- "dependencies"
24
- ],
25
- "author": "Bob Frankston",
26
- "license": "MIT",
27
- "repository": {
28
- "type": "git",
29
- "url": "https://github.com/BobFrankston/importgen.git"
30
- },
31
- "dependencies": {
32
- "chokidar": "^4.0.3"
33
- },
34
- "devDependencies": {
35
- "@types/node": "^25.0.9"
36
- }
37
- }
1
+ {
2
+ "name": "@bobfrankston/importgen",
3
+ "version": "0.1.15",
4
+ "description": "Generate ES Module import maps from package.json dependencies for native browser module loading",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "importgen": "index.js"
8
+ },
9
+ "type": "module",
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "watch": "tsc -w",
13
+ "preversion": "npm run build",
14
+ "postversion": "echo Version updated to $(node -p \"require('./package.json').version\")",
15
+ "release": "git add -A && git diff-index --quiet HEAD || git commit -m 'Build for release' && npm version patch",
16
+ "installer": "npm run release && npm install -g ."
17
+ },
18
+ "keywords": [
19
+ "import-map",
20
+ "esm",
21
+ "browser",
22
+ "modules",
23
+ "dependencies"
24
+ ],
25
+ "author": "Bob Frankston",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/BobFrankston/importgen.git"
30
+ },
31
+ "dependencies": {
32
+ "chokidar": "^4.0.3"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^25.2.1"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ }
40
+ }