browser-metro 1.0.5
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/dist/bundler.d.ts +34 -0
- package/dist/bundler.js +320 -0
- package/dist/dependency-graph.d.ts +22 -0
- package/dist/dependency-graph.js +128 -0
- package/dist/fs.d.ts +20 -0
- package/dist/fs.js +107 -0
- package/dist/hmr-runtime.d.ts +14 -0
- package/dist/hmr-runtime.js +231 -0
- package/dist/incremental-bundler.d.ts +71 -0
- package/dist/incremental-bundler.js +646 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +9 -0
- package/dist/module-cache.d.ts +22 -0
- package/dist/module-cache.js +31 -0
- package/dist/plugins/data-bx-path.d.ts +2 -0
- package/dist/plugins/data-bx-path.js +197 -0
- package/dist/resolver.d.ts +18 -0
- package/dist/resolver.js +84 -0
- package/dist/source-map.d.ts +36 -0
- package/dist/source-map.js +186 -0
- package/dist/transforms/react-refresh.d.ts +5 -0
- package/dist/transforms/react-refresh.js +92 -0
- package/dist/transforms/typescript.d.ts +2 -0
- package/dist/transforms/typescript.js +20 -0
- package/dist/types.d.ts +99 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +31 -0
- package/dist/utils.js +208 -0
- package/package.json +22 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { RawSourceMap } from "./source-map.js";
|
|
2
|
+
export interface FileEntry {
|
|
3
|
+
content: string;
|
|
4
|
+
isExternal: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface FileMap {
|
|
7
|
+
[path: string]: FileEntry;
|
|
8
|
+
}
|
|
9
|
+
export interface ModuleMap {
|
|
10
|
+
[id: string]: string;
|
|
11
|
+
}
|
|
12
|
+
export interface TransformResult {
|
|
13
|
+
code: string;
|
|
14
|
+
sourceMap?: RawSourceMap;
|
|
15
|
+
}
|
|
16
|
+
export interface TransformParams {
|
|
17
|
+
src: string;
|
|
18
|
+
filename: string;
|
|
19
|
+
}
|
|
20
|
+
export interface Transformer {
|
|
21
|
+
transform(params: TransformParams): TransformResult;
|
|
22
|
+
}
|
|
23
|
+
export interface ResolverConfig {
|
|
24
|
+
sourceExts: string[];
|
|
25
|
+
paths?: Record<string, string[]>;
|
|
26
|
+
}
|
|
27
|
+
export interface BundlerPlugin {
|
|
28
|
+
name: string;
|
|
29
|
+
/** Runs BEFORE Sucrase. Receives raw .tsx/.ts source (JSX still intact). */
|
|
30
|
+
transformSource?(params: {
|
|
31
|
+
src: string;
|
|
32
|
+
filename: string;
|
|
33
|
+
}): {
|
|
34
|
+
src: string;
|
|
35
|
+
} | null;
|
|
36
|
+
/** Runs AFTER Sucrase. Receives CommonJS output. */
|
|
37
|
+
transformOutput?(params: {
|
|
38
|
+
code: string;
|
|
39
|
+
filename: string;
|
|
40
|
+
}): {
|
|
41
|
+
code: string;
|
|
42
|
+
} | null;
|
|
43
|
+
/** Custom module resolution. Return a resolved path or npm name, or null to fall through. */
|
|
44
|
+
resolveRequest?(context: {
|
|
45
|
+
fromFile: string;
|
|
46
|
+
}, moduleName: string): string | null;
|
|
47
|
+
/**
|
|
48
|
+
* Module aliases. Returns a map of `{ source: target }`.
|
|
49
|
+
* The bundler injects shim modules so `require(source)` re-exports `target`.
|
|
50
|
+
* Works for ALL require calls -- local files and npm packages alike.
|
|
51
|
+
*/
|
|
52
|
+
moduleAliases?(): Record<string, string>;
|
|
53
|
+
/**
|
|
54
|
+
* Module shims. Returns a map of `{ moduleName: inlineCode }`.
|
|
55
|
+
* Replaces npm packages with lightweight inline implementations.
|
|
56
|
+
* Shimmed modules are NOT fetched from the package server.
|
|
57
|
+
*/
|
|
58
|
+
shimModules?(): Record<string, string>;
|
|
59
|
+
}
|
|
60
|
+
export interface BundlerConfig {
|
|
61
|
+
resolver: ResolverConfig;
|
|
62
|
+
transformer: Transformer;
|
|
63
|
+
server: {
|
|
64
|
+
packageServerUrl: string;
|
|
65
|
+
};
|
|
66
|
+
hmr?: {
|
|
67
|
+
enabled: boolean;
|
|
68
|
+
reactRefresh?: boolean;
|
|
69
|
+
};
|
|
70
|
+
plugins?: BundlerPlugin[];
|
|
71
|
+
env?: Record<string, string>;
|
|
72
|
+
routerShim?: boolean;
|
|
73
|
+
/** URL prefix for external assets, e.g. "/projects/expo-real" */
|
|
74
|
+
assetPublicPath?: string;
|
|
75
|
+
}
|
|
76
|
+
export interface FileChange {
|
|
77
|
+
path: string;
|
|
78
|
+
type: "create" | "update" | "delete";
|
|
79
|
+
}
|
|
80
|
+
export interface ContentChange {
|
|
81
|
+
path: string;
|
|
82
|
+
type: "create" | "update" | "delete";
|
|
83
|
+
content?: string;
|
|
84
|
+
}
|
|
85
|
+
export interface HmrUpdate {
|
|
86
|
+
updatedModules: Record<string, string>;
|
|
87
|
+
removedModules: string[];
|
|
88
|
+
requiresReload: boolean;
|
|
89
|
+
reloadReason?: string;
|
|
90
|
+
reverseDepsMap?: Record<string, string[]>;
|
|
91
|
+
}
|
|
92
|
+
export interface IncrementalBuildResult {
|
|
93
|
+
bundle: string;
|
|
94
|
+
hmrUpdate: HmrUpdate | null;
|
|
95
|
+
type: "full" | "incremental";
|
|
96
|
+
rebuiltModules: string[];
|
|
97
|
+
removedModules: string[];
|
|
98
|
+
buildTime: number;
|
|
99
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Extract all require('...') call targets from source */
|
|
2
|
+
export declare function findRequires(source: string): string[];
|
|
3
|
+
/**
|
|
4
|
+
* Rewrite require calls using a resolve callback.
|
|
5
|
+
* resolveTarget returns a string to rewrite to, or null to leave unchanged.
|
|
6
|
+
*/
|
|
7
|
+
export declare function rewriteRequires(source: string, fromFile: string, resolveTarget: (target: string) => string | null): string;
|
|
8
|
+
/**
|
|
9
|
+
* Build the bundle preamble (Metro-style). Defines `process` global so npm
|
|
10
|
+
* packages that check `process.env.NODE_ENV` work in the browser.
|
|
11
|
+
* Optionally injects public env vars (EXPO_PUBLIC_*, NEXT_PUBLIC_*).
|
|
12
|
+
* Optionally appends the router shim for virtualizing History/Location APIs.
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildBundlePreamble(env?: Record<string, string>, routerShim?: boolean): string;
|
|
15
|
+
/**
|
|
16
|
+
* Build a self-contained IIFE that virtualizes the History API.
|
|
17
|
+
*
|
|
18
|
+
* The iframe is loaded via a blob URL with the initial route encoded in the
|
|
19
|
+
* hash fragment (e.g. blob:origin/uuid#/explore). Location properties are
|
|
20
|
+
* [LegacyUnforgeable] so we can't override them, but we CAN call the real
|
|
21
|
+
* replaceState to change the blob URL's pathname from the UUID to the
|
|
22
|
+
* virtual path. After that, location.pathname returns the correct value
|
|
23
|
+
* for Expo Router's initial route match.
|
|
24
|
+
*
|
|
25
|
+
* All subsequent pushState/replaceState calls are intercepted so no further
|
|
26
|
+
* real URL changes occur. The current virtual route is stored in
|
|
27
|
+
* window.__ROUTER_SHIM_HASH__ for the parent to read across iframe rebuilds.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildRouterShim(): string;
|
|
30
|
+
/** Fast djb2 hash for cache invalidation */
|
|
31
|
+
export declare function hashString(str: string): string;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
const REQUIRE_RE = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
2
|
+
// Matches lines that are single-line comments or JSDoc/block comment continuations
|
|
3
|
+
const COMMENT_LINE_RE = /^\s*(?:\/\/|\/?\*)/;
|
|
4
|
+
/** Extract all require('...') call targets from source */
|
|
5
|
+
export function findRequires(source) {
|
|
6
|
+
if (!source)
|
|
7
|
+
return [];
|
|
8
|
+
const requires = [];
|
|
9
|
+
const lines = source.split("\n");
|
|
10
|
+
for (let i = 0; i < lines.length; i++) {
|
|
11
|
+
const line = lines[i];
|
|
12
|
+
// Skip comment lines to avoid picking up require() in JSDoc examples
|
|
13
|
+
if (COMMENT_LINE_RE.test(line))
|
|
14
|
+
continue;
|
|
15
|
+
const re = new RegExp(REQUIRE_RE.source, REQUIRE_RE.flags);
|
|
16
|
+
let match;
|
|
17
|
+
while ((match = re.exec(line)) !== null) {
|
|
18
|
+
requires.push(match[1]);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return requires;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Rewrite require calls using a resolve callback.
|
|
25
|
+
* resolveTarget returns a string to rewrite to, or null to leave unchanged.
|
|
26
|
+
*/
|
|
27
|
+
export function rewriteRequires(source, fromFile, resolveTarget) {
|
|
28
|
+
return source.replace(REQUIRE_RE, (full, target) => {
|
|
29
|
+
const resolved = resolveTarget(target);
|
|
30
|
+
if (resolved === null)
|
|
31
|
+
return full;
|
|
32
|
+
return 'require("' + resolved + '")';
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const PUBLIC_ENV_PREFIXES = ["EXPO_PUBLIC_", "NEXT_PUBLIC_"];
|
|
36
|
+
/**
|
|
37
|
+
* Build the bundle preamble (Metro-style). Defines `process` global so npm
|
|
38
|
+
* packages that check `process.env.NODE_ENV` work in the browser.
|
|
39
|
+
* Optionally injects public env vars (EXPO_PUBLIC_*, NEXT_PUBLIC_*).
|
|
40
|
+
* Optionally appends the router shim for virtualizing History/Location APIs.
|
|
41
|
+
*/
|
|
42
|
+
export function buildBundlePreamble(env, routerShim) {
|
|
43
|
+
let preamble = "var process = globalThis.process || {};\n" +
|
|
44
|
+
"process.env = process.env || {};\n" +
|
|
45
|
+
'process.env.NODE_ENV = process.env.NODE_ENV || "development";\n';
|
|
46
|
+
if (env) {
|
|
47
|
+
for (const [key, value] of Object.entries(env)) {
|
|
48
|
+
if (PUBLIC_ENV_PREFIXES.some((p) => key.startsWith(p))) {
|
|
49
|
+
preamble += "process.env." + key + " = " + JSON.stringify(value) + ";\n";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (routerShim) {
|
|
54
|
+
preamble += buildRouterShim();
|
|
55
|
+
}
|
|
56
|
+
return preamble;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build a self-contained IIFE that virtualizes the History API.
|
|
60
|
+
*
|
|
61
|
+
* The iframe is loaded via a blob URL with the initial route encoded in the
|
|
62
|
+
* hash fragment (e.g. blob:origin/uuid#/explore). Location properties are
|
|
63
|
+
* [LegacyUnforgeable] so we can't override them, but we CAN call the real
|
|
64
|
+
* replaceState to change the blob URL's pathname from the UUID to the
|
|
65
|
+
* virtual path. After that, location.pathname returns the correct value
|
|
66
|
+
* for Expo Router's initial route match.
|
|
67
|
+
*
|
|
68
|
+
* All subsequent pushState/replaceState calls are intercepted so no further
|
|
69
|
+
* real URL changes occur. The current virtual route is stored in
|
|
70
|
+
* window.__ROUTER_SHIM_HASH__ for the parent to read across iframe rebuilds.
|
|
71
|
+
*/
|
|
72
|
+
export function buildRouterShim() {
|
|
73
|
+
return `(function() {
|
|
74
|
+
var rawHash = location.hash.slice(1) || '/';
|
|
75
|
+
|
|
76
|
+
var hashIdx = rawHash.indexOf('#');
|
|
77
|
+
var virtualHash = '';
|
|
78
|
+
var pathAndSearch = rawHash;
|
|
79
|
+
if (hashIdx > 0) {
|
|
80
|
+
virtualHash = rawHash.slice(hashIdx);
|
|
81
|
+
pathAndSearch = rawHash.slice(0, hashIdx);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
var searchIdx = pathAndSearch.indexOf('?');
|
|
85
|
+
var virtualPathname = searchIdx >= 0 ? pathAndSearch.slice(0, searchIdx) : pathAndSearch;
|
|
86
|
+
var virtualSearch = searchIdx >= 0 ? pathAndSearch.slice(searchIdx) : '';
|
|
87
|
+
|
|
88
|
+
if (virtualPathname.charAt(0) !== '/') virtualPathname = '/' + virtualPathname;
|
|
89
|
+
|
|
90
|
+
var virtualOrigin = location.origin || 'http://localhost';
|
|
91
|
+
var virtualHref = virtualOrigin + virtualPathname + virtualSearch + virtualHash;
|
|
92
|
+
|
|
93
|
+
// Override document.URL (configurable, unlike location.*)
|
|
94
|
+
try {
|
|
95
|
+
Object.defineProperty(Document.prototype, 'URL', {
|
|
96
|
+
get: function() { return virtualHref; },
|
|
97
|
+
configurable: true
|
|
98
|
+
});
|
|
99
|
+
} catch(e) {}
|
|
100
|
+
|
|
101
|
+
// Wrap URL constructor so new URL(location.href) returns the virtual URL
|
|
102
|
+
// instead of parsing the blob UUID as a pathname.
|
|
103
|
+
var OrigURL = URL;
|
|
104
|
+
var _URL = function(url, base) {
|
|
105
|
+
var u = String(url);
|
|
106
|
+
if (u.indexOf('blob:') === 0) u = virtualHref;
|
|
107
|
+
if (arguments.length > 1) return new OrigURL(u, base);
|
|
108
|
+
return new OrigURL(u);
|
|
109
|
+
};
|
|
110
|
+
_URL.prototype = OrigURL.prototype;
|
|
111
|
+
_URL.createObjectURL = OrigURL.createObjectURL;
|
|
112
|
+
_URL.revokeObjectURL = OrigURL.revokeObjectURL;
|
|
113
|
+
_URL.canParse = OrigURL.canParse;
|
|
114
|
+
window.URL = _URL;
|
|
115
|
+
|
|
116
|
+
// Save real replaceState before overriding
|
|
117
|
+
var _realReplaceState = history.replaceState;
|
|
118
|
+
// Base blob URL without hash, for constructing absolute URLs in sync()
|
|
119
|
+
var _blobBase = location.href.split('#')[0];
|
|
120
|
+
|
|
121
|
+
var stack = [{ state: null, pathname: virtualPathname, search: virtualSearch, hash: virtualHash }];
|
|
122
|
+
var stackIndex = 0;
|
|
123
|
+
|
|
124
|
+
function currentEntry() { return stack[stackIndex]; }
|
|
125
|
+
|
|
126
|
+
function sync() {
|
|
127
|
+
var e = currentEntry();
|
|
128
|
+
virtualPathname = e.pathname;
|
|
129
|
+
virtualSearch = e.search;
|
|
130
|
+
virtualHash = e.hash;
|
|
131
|
+
virtualHref = virtualOrigin + virtualPathname + virtualSearch + virtualHash;
|
|
132
|
+
var routeHash = '#' + e.pathname + e.search;
|
|
133
|
+
window.__ROUTER_SHIM_HASH__ = routeHash;
|
|
134
|
+
// Update the real blob URL hash, but only if it actually changed.
|
|
135
|
+
var newUrl = _blobBase + routeHash;
|
|
136
|
+
if (newUrl !== location.href) {
|
|
137
|
+
try { _realReplaceState.call(history, e.state, '', newUrl); } catch(e) {}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function parseUrl(url) {
|
|
142
|
+
var s = String(url);
|
|
143
|
+
if (s.indexOf('blob:') === 0) {
|
|
144
|
+
try { var inner = new OrigURL(s.slice(5)); s = inner.pathname + inner.search + inner.hash; } catch(e) {}
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
var u = new OrigURL(s, virtualOrigin);
|
|
148
|
+
return { pathname: u.pathname, search: u.search, hash: u.hash };
|
|
149
|
+
} catch(e) {}
|
|
150
|
+
var pathname = s, search = '', hash = '';
|
|
151
|
+
var hi = pathname.indexOf('#');
|
|
152
|
+
if (hi >= 0) { hash = pathname.slice(hi); pathname = pathname.slice(0, hi); }
|
|
153
|
+
var si = pathname.indexOf('?');
|
|
154
|
+
if (si >= 0) { search = pathname.slice(si); pathname = pathname.slice(0, si); }
|
|
155
|
+
if (!pathname) pathname = '/';
|
|
156
|
+
if (pathname.charAt(0) !== '/') pathname = '/' + pathname;
|
|
157
|
+
return { pathname: pathname, search: search, hash: hash };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
history.pushState = function(state, title, url) {
|
|
161
|
+
if (url != null) {
|
|
162
|
+
var p = parseUrl(url);
|
|
163
|
+
// Ignore hash: it's our own route hash echoed back from location.hash
|
|
164
|
+
stack = stack.slice(0, stackIndex + 1);
|
|
165
|
+
stack.push({ state: state, pathname: p.pathname, search: p.search, hash: '' });
|
|
166
|
+
stackIndex = stack.length - 1;
|
|
167
|
+
sync();
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
history.replaceState = function(state, title, url) {
|
|
172
|
+
if (url != null) {
|
|
173
|
+
var p = parseUrl(url);
|
|
174
|
+
stack[stackIndex] = { state: state, pathname: p.pathname, search: p.search, hash: '' };
|
|
175
|
+
sync();
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
history.go = function(n) {
|
|
180
|
+
if (!n) return;
|
|
181
|
+
var ni = stackIndex + n;
|
|
182
|
+
if (ni < 0) ni = 0;
|
|
183
|
+
if (ni >= stack.length) ni = stack.length - 1;
|
|
184
|
+
if (ni === stackIndex) return;
|
|
185
|
+
stackIndex = ni;
|
|
186
|
+
sync();
|
|
187
|
+
window.dispatchEvent(new PopStateEvent('popstate', { state: currentEntry().state }));
|
|
188
|
+
};
|
|
189
|
+
history.back = function() { history.go(-1); };
|
|
190
|
+
history.forward = function() { history.go(1); };
|
|
191
|
+
|
|
192
|
+
Object.defineProperty(history, 'state', {
|
|
193
|
+
get: function() { return currentEntry().state; },
|
|
194
|
+
configurable: true
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
sync();
|
|
198
|
+
})();
|
|
199
|
+
`;
|
|
200
|
+
}
|
|
201
|
+
/** Fast djb2 hash for cache invalidation */
|
|
202
|
+
export function hashString(str) {
|
|
203
|
+
let hash = 5381;
|
|
204
|
+
for (let i = 0; i < str.length; i++) {
|
|
205
|
+
hash = ((hash << 5) + hash + str.charCodeAt(i)) | 0;
|
|
206
|
+
}
|
|
207
|
+
return (hash >>> 0).toString(36);
|
|
208
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "browser-metro",
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "A browser-based JavaScript/TypeScript bundler with HMR support",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"prepublishOnly": "tsc"
|
|
14
|
+
},
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"sucrase": "^3.35.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"typescript": "^5.7.0"
|
|
21
|
+
}
|
|
22
|
+
}
|