@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.
- package/.claude/settings.local.json +2 -1
- package/README.md +151 -49
- package/index.js +12 -27
- package/package.json +40 -37
package/README.md
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
# @bobfrankston/importgen
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use the same npm packages and `import` statements in the browser as in Node.js/Bun -- no bundler required.
|
|
4
4
|
|
|
5
|
-
##
|
|
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
|
-
|
|
25
|
+
Run from your project directory (containing `package.json` and your HTML file):
|
|
14
26
|
|
|
15
27
|
```bash
|
|
16
|
-
|
|
28
|
+
importgen [htmlfile] [--watch|-w]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Generate import map in index.html (default)
|
|
17
33
|
importgen
|
|
18
34
|
|
|
19
|
-
#
|
|
20
|
-
importgen
|
|
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
|
-
|
|
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
|
-
|
|
50
|
+
## What It Generates
|
|
51
|
+
|
|
52
|
+
Given a `package.json` like:
|
|
26
53
|
|
|
27
54
|
```json
|
|
28
55
|
{
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
94
|
+
## How Entry Points Are Resolved
|
|
57
95
|
|
|
58
|
-
|
|
96
|
+
For each dependency, `importgen` reads that package's `package.json` and picks the entry point in this order:
|
|
59
97
|
|
|
60
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
"
|
|
71
|
-
|
|
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
|
-
|
|
124
|
+
The import map will use the `.dependencies` path instead of the `node_modules` path for that package.
|
|
77
125
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
174
|
+
]
|
|
175
|
+
}
|
|
89
176
|
```
|
|
90
177
|
|
|
91
|
-
|
|
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
|
-
//
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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.
|
|
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.
|
|
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
|
+
}
|