bippy 0.5.27 → 0.5.29

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.
Files changed (43) hide show
  1. package/README.md +0 -2
  2. package/dist/{core-BDWE7M7e.d.ts → core-Cjoce0EW.d.ts} +2 -2
  3. package/dist/core-DBBh-FTl.js +9 -0
  4. package/dist/{core-CEUgwvkw.d.cts → core-Y1ecSyti.d.cts} +2 -2
  5. package/dist/core-xjGqMMEY.cjs +9 -0
  6. package/dist/core.cjs +1 -1
  7. package/dist/core.d.cts +1 -1
  8. package/dist/core.d.ts +1 -1
  9. package/dist/core.js +1 -1
  10. package/dist/index.cjs +1 -1
  11. package/dist/index.d.cts +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.iife.js +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/{install-hook-only-BOBPiBkc.js → install-hook-only-CRye53Kz.cjs} +1 -1
  16. package/dist/{install-hook-only-aVNdwrnj.cjs → install-hook-only-DfpahZZO.js} +1 -1
  17. package/dist/install-hook-only.cjs +1 -1
  18. package/dist/install-hook-only.iife.js +1 -1
  19. package/dist/install-hook-only.js +1 -1
  20. package/dist/rdt-hook-Czn6qzdx.js +9 -0
  21. package/dist/rdt-hook-DnMMBqZs.cjs +9 -0
  22. package/dist/source.cjs +7 -7
  23. package/dist/source.d.cts +1 -1
  24. package/dist/source.d.ts +1 -1
  25. package/dist/source.js +12 -12
  26. package/package.json +3 -1
  27. package/src/core.ts +1325 -0
  28. package/src/index.ts +3 -0
  29. package/src/install-hook-only.ts +3 -0
  30. package/src/rdt-hook.ts +237 -0
  31. package/src/source/constants.ts +26 -0
  32. package/src/source/get-display-name-from-source.ts +103 -0
  33. package/src/source/get-source.ts +218 -0
  34. package/src/source/index.ts +6 -0
  35. package/src/source/owner-stack.ts +598 -0
  36. package/src/source/parse-stack.ts +295 -0
  37. package/src/source/symbolication.ts +410 -0
  38. package/src/source/types.ts +6 -0
  39. package/src/types.ts +244 -0
  40. package/dist/core-D8j-0_U5.cjs +0 -9
  41. package/dist/core-coQbWNwP.js +0 -9
  42. package/dist/rdt-hook-3SlCAu5p.cjs +0 -9
  43. package/dist/rdt-hook-BZMdLD7S.js +0 -9
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ import './install-hook-only.js';
2
+
3
+ export * from './core.js';
@@ -0,0 +1,3 @@
1
+ import { safelyInstallRDTHook } from './rdt-hook.js';
2
+
3
+ safelyInstallRDTHook();
@@ -0,0 +1,237 @@
1
+ // IMPORTANT:
2
+ // this file is super important to load the __REACT_DEVTOOLS_GLOBAL_HOOK__ object
3
+ // without this, we can't stub the React DevTools global hook, we don't have a way to instrument the application
4
+ // make sure you import this file first before anything else (particularly React)
5
+
6
+ import type { ReactDevToolsGlobalHook, ReactRenderer } from './types.js';
7
+
8
+ export const version = process.env.VERSION;
9
+ export const BIPPY_INSTRUMENTATION_STRING = `bippy-${version}`;
10
+
11
+ const objectDefineProperty = Object.defineProperty;
12
+ // eslint-disable-next-line @typescript-eslint/unbound-method
13
+ const objectHasOwnProperty = Object.prototype.hasOwnProperty;
14
+
15
+ const NO_OP = () => {
16
+ /**/
17
+ };
18
+
19
+ const checkDCE = (fn: unknown): void => {
20
+ try {
21
+ const code = Function.prototype.toString.call(fn);
22
+ if (code.indexOf('^_^') > -1) {
23
+ setTimeout(() => {
24
+ throw new Error(
25
+ 'React is running in production mode, but dead code ' +
26
+ 'elimination has not been applied. Read how to correctly ' +
27
+ 'configure React for production: ' +
28
+ 'https://reactjs.org/link/perf-use-production-build',
29
+ );
30
+ });
31
+ }
32
+ } catch {}
33
+ };
34
+
35
+ export const isRealReactDevtools = (
36
+ rdtHook: ReactDevToolsGlobalHook | undefined | null = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__,
37
+ ): boolean => {
38
+ return Boolean(rdtHook && 'getFiberRoots' in rdtHook);
39
+ };
40
+
41
+ let isReactRefreshOverride = false;
42
+ let injectFnStr: string | undefined = undefined;
43
+
44
+ export const isReactRefresh = (
45
+ rdtHook: ReactDevToolsGlobalHook | undefined | null = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__,
46
+ ): boolean => {
47
+ if (isReactRefreshOverride) return true;
48
+ if (rdtHook && typeof rdtHook.inject === 'function') {
49
+ injectFnStr = rdtHook.inject.toString();
50
+ }
51
+ // https://github.com/facebook/react/blob/8f8b336734d7c807f5aa11b0f31540e63302d789/packages/react-refresh/src/ReactFreshRuntime.js#L459
52
+ return Boolean(injectFnStr?.includes('(injected)'));
53
+ };
54
+
55
+ const onActiveListeners = new Set<() => unknown>();
56
+
57
+ export const _renderers = new Set<ReactRenderer>();
58
+
59
+ export const installRDTHook = (
60
+ onActive?: () => unknown,
61
+ ): ReactDevToolsGlobalHook => {
62
+ const renderers = new Map<number, ReactRenderer>();
63
+ let i = 0;
64
+ let rdtHook: ReactDevToolsGlobalHook = {
65
+ _instrumentationIsActive: false,
66
+ _instrumentationSource: BIPPY_INSTRUMENTATION_STRING,
67
+ checkDCE,
68
+ hasUnsupportedRendererAttached: false,
69
+ inject(renderer) {
70
+ const nextID = ++i;
71
+ renderers.set(nextID, renderer);
72
+ _renderers.add(renderer);
73
+ if (!rdtHook._instrumentationIsActive) {
74
+ rdtHook._instrumentationIsActive = true;
75
+ onActiveListeners.forEach((listener) => listener());
76
+ }
77
+ return nextID;
78
+ },
79
+ on: NO_OP,
80
+ onCommitFiberRoot: NO_OP,
81
+ onCommitFiberUnmount: NO_OP,
82
+ onPostCommitFiberRoot: NO_OP,
83
+ renderers,
84
+ supportsFiber: true,
85
+ supportsFlight: true,
86
+ };
87
+ try {
88
+ objectDefineProperty(globalThis, '__REACT_DEVTOOLS_GLOBAL_HOOK__', {
89
+ configurable: true,
90
+ enumerable: true,
91
+ get() {
92
+ return rdtHook;
93
+ },
94
+ set(newHook) {
95
+ if (newHook && typeof newHook === 'object') {
96
+ const ourRenderers = rdtHook.renderers;
97
+ rdtHook = newHook;
98
+ if (ourRenderers.size > 0) {
99
+ ourRenderers.forEach((renderer, id) => {
100
+ _renderers.add(renderer);
101
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
102
+ newHook.renderers.set(id, renderer);
103
+ });
104
+ patchRDTHook(onActive);
105
+ }
106
+ }
107
+ },
108
+ });
109
+ // [!] this is a hack for chrome extensions - if we install before React DevTools, we could accidently prevent React DevTools from installing:
110
+ // https://github.com/facebook/react/blob/18eaf51bd51fed8dfed661d64c306759101d0bfd/packages/react-devtools-extensions/src/contentScripts/installHook.js#L30C6-L30C27
111
+ // eslint-disable-next-line @typescript-eslint/unbound-method
112
+ const originalWindowHasOwnProperty = window.hasOwnProperty;
113
+ let hasRanHack = false;
114
+ objectDefineProperty(window, 'hasOwnProperty', {
115
+ configurable: true,
116
+ value: function (this: unknown, ...args: [PropertyKey]) {
117
+ try {
118
+ if (!hasRanHack && args[0] === '__REACT_DEVTOOLS_GLOBAL_HOOK__') {
119
+ globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__ = undefined;
120
+ // special falsy value to know that we've already installed before
121
+ hasRanHack = true;
122
+ return -0;
123
+ }
124
+ } catch {}
125
+ return originalWindowHasOwnProperty.apply(this, args);
126
+ },
127
+ writable: true,
128
+ });
129
+ } catch {
130
+ patchRDTHook(onActive);
131
+ }
132
+ return rdtHook;
133
+ };
134
+
135
+ export const patchRDTHook = (onActive?: () => unknown): void => {
136
+ if (onActive) {
137
+ onActiveListeners.add(onActive);
138
+ }
139
+ try {
140
+ const rdtHook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
141
+ if (!rdtHook) return;
142
+ if (!rdtHook._instrumentationSource) {
143
+ rdtHook.checkDCE = checkDCE;
144
+ rdtHook.supportsFiber = true;
145
+ rdtHook.supportsFlight = true;
146
+ rdtHook.hasUnsupportedRendererAttached = false;
147
+ rdtHook._instrumentationSource = BIPPY_INSTRUMENTATION_STRING;
148
+ rdtHook._instrumentationIsActive = false;
149
+ // we need to be careful here (needs to be below _instrumentationSource) else it causes excessive recursion
150
+ const isReactDevtools = isRealReactDevtools(rdtHook);
151
+ if (!isReactDevtools) {
152
+ rdtHook.on = NO_OP;
153
+ }
154
+ if (rdtHook.renderers.size) {
155
+ rdtHook._instrumentationIsActive = true;
156
+ onActiveListeners.forEach((listener) => listener());
157
+ return;
158
+ }
159
+ const prevInject = rdtHook.inject;
160
+ const isRefresh = isReactRefresh(rdtHook);
161
+ if (isRefresh && !isReactDevtools) {
162
+ isReactRefreshOverride = true;
163
+ // but since the underlying implementation doens't care,
164
+ // it's ok: https://github.com/facebook/react/blob/18eaf51bd51fed8dfed661d64c306759101d0bfd/packages/react-refresh/src/ReactFreshRuntime.js#L430
165
+ const nextID = rdtHook.inject({
166
+ scheduleRefresh() {},
167
+ } as unknown as ReactRenderer);
168
+ if (nextID) {
169
+ rdtHook._instrumentationIsActive = true;
170
+ }
171
+ }
172
+ rdtHook.inject = (renderer) => {
173
+ const id = prevInject(renderer);
174
+ _renderers.add(renderer);
175
+ if (isRefresh) {
176
+ // react refresh doesn't inject this properly
177
+ // https://github.com/facebook/react/blob/18eaf51bd51fed8dfed661d64c306759101d0bfd/packages/react-refresh/src/ReactFreshRuntime.js#L430
178
+ rdtHook.renderers.set(id, renderer);
179
+ }
180
+ rdtHook._instrumentationIsActive = true;
181
+ onActiveListeners.forEach((listener) => listener());
182
+ return id;
183
+ };
184
+ }
185
+ if (
186
+ rdtHook.renderers.size ||
187
+ rdtHook._instrumentationIsActive ||
188
+ // depending on this to inject is unsafe, since inject could occur before and we wouldn't know
189
+ isReactRefresh()
190
+ ) {
191
+ onActive?.();
192
+ }
193
+ } catch {}
194
+ };
195
+
196
+ export const hasRDTHook = (): boolean => {
197
+ return objectHasOwnProperty.call(
198
+ globalThis,
199
+ '__REACT_DEVTOOLS_GLOBAL_HOOK__',
200
+ );
201
+ };
202
+
203
+ /**
204
+ * Returns the current React DevTools global hook.
205
+ */
206
+ export const getRDTHook = (
207
+ onActive?: () => unknown,
208
+ ): ReactDevToolsGlobalHook => {
209
+ if (!hasRDTHook()) {
210
+ return installRDTHook(onActive);
211
+ }
212
+
213
+ patchRDTHook(onActive);
214
+ // must exist at this point
215
+ return globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__ as ReactDevToolsGlobalHook;
216
+ };
217
+
218
+ export const isClientEnvironment = (): boolean => {
219
+ return Boolean(
220
+ typeof window !== 'undefined' &&
221
+ // eslint-disable-next-line @typescript-eslint/unbound-method
222
+ (window.document?.createElement ||
223
+ window.navigator?.product === 'ReactNative'),
224
+ );
225
+ };
226
+
227
+ /**
228
+ * Usually used purely for side effect
229
+ */
230
+ export const safelyInstallRDTHook = () => {
231
+ try {
232
+ // __REACT_DEVTOOLS_GLOBAL_HOOK__ must exist before React is ever executed
233
+ if (isClientEnvironment()) {
234
+ getRDTHook();
235
+ }
236
+ } catch {}
237
+ };
@@ -0,0 +1,26 @@
1
+ export const SCHEME_REGEX = /^[a-zA-Z][a-zA-Z\d+\-.]*:/;
2
+
3
+ export const INTERNAL_SCHEME_PREFIXES = [
4
+ 'rsc://',
5
+ 'file:///',
6
+ 'webpack://',
7
+ 'webpack-internal://',
8
+ 'node:',
9
+ 'turbopack://',
10
+ 'metro://',
11
+ '/app-pages-browser/',
12
+ ] as const;
13
+
14
+ export const ABOUT_REACT_PREFIX = 'about://React/';
15
+
16
+ export const ANONYMOUS_FILE_PATTERNS = ['<anonymous>', 'eval', ''] as const;
17
+
18
+ export const SOURCE_FILE_EXTENSION_REGEX = /\.(jsx|tsx|ts|js)$/;
19
+
20
+ export const BUNDLED_FILE_PATTERN_REGEX =
21
+ /(\.min|bundle|chunk|vendor|vendors|runtime|polyfill|polyfills)\.(js|mjs|cjs)$|(chunk|bundle|vendor|vendors|runtime|polyfill|polyfills|framework|app|main|index)[-_.][A-Za-z0-9_-]{4,}\.(js|mjs|cjs)$|[\da-f]{8,}\.(js|mjs|cjs)$|[-_.][\da-f]{20,}\.(js|mjs|cjs)$|\/dist\/|\/build\/|\/.next\/|\/out\/|\/node_modules\/|\.webpack\.|\.vite\.|\.turbopack\./i;
22
+
23
+ export const QUERY_PARAMETER_PATTERN_REGEX =
24
+ /^\?[\w~.-]+(?:=[^&#]*)?(?:&[\w~.-]+(?:=[^&#]*)?)*$/;
25
+
26
+ export const SERVER_FRAME_MARKER = '(at Server)';
@@ -0,0 +1,103 @@
1
+ import { Fiber } from '../types.js';
2
+ import { getDisplayName } from '../core.js';
3
+ import { getOwnerStack } from './owner-stack.js';
4
+ import { getSourceFromSourceMap, getSourceMap } from './symbolication.js';
5
+ import { StackFrame } from './parse-stack.js';
6
+
7
+ const extractComponentNameFromSource = (
8
+ sourceContent: string,
9
+ lineNumber: number,
10
+ ): string | null => {
11
+ const lines = sourceContent.split('\n');
12
+ const targetLineIndex = lineNumber - 1;
13
+
14
+ if (targetLineIndex < 0 || targetLineIndex >= lines.length) {
15
+ return null;
16
+ }
17
+
18
+ const startLine = Math.max(0, targetLineIndex - 5);
19
+ const endLine = Math.min(lines.length, targetLineIndex + 5);
20
+ const contextLines = lines.slice(startLine, endLine).join('\n');
21
+
22
+ const arrowFunctionPattern = /(?:^|export\s+)(?:const|let|var)\s+(\w+)\s*=/m;
23
+ const functionPattern = /(?:^|export\s+)function\s+(\w+)/m;
24
+ const classPattern = /(?:^|export\s+)class\s+(\w+)/m;
25
+
26
+ const arrowMatch = contextLines.match(arrowFunctionPattern);
27
+ if (arrowMatch?.[1]) {
28
+ return arrowMatch[1];
29
+ }
30
+
31
+ const functionMatch = contextLines.match(functionPattern);
32
+ if (functionMatch?.[1]) {
33
+ return functionMatch[1];
34
+ }
35
+
36
+ const classMatch = contextLines.match(classPattern);
37
+ if (classMatch?.[1]) {
38
+ return classMatch[1];
39
+ }
40
+
41
+ return null;
42
+ };
43
+
44
+ export const getDisplayNameFromSource = async (
45
+ fiber: Fiber,
46
+ cache = true,
47
+ fetchFn?: (url: string) => Promise<Response>,
48
+ ): Promise<string | null> => {
49
+ const ownerStack = await getOwnerStack(fiber, cache, fetchFn);
50
+ const stackFrame = ownerStack.filter((stackFrame) => stackFrame.fileName)[0];
51
+
52
+ if (!stackFrame?.fileName) {
53
+ return getDisplayName(fiber.type);
54
+ }
55
+
56
+ const bundleSourceMap = await getSourceMap(
57
+ stackFrame.fileName,
58
+ cache,
59
+ fetchFn,
60
+ );
61
+
62
+ if (!bundleSourceMap) {
63
+ return getDisplayName(fiber.type);
64
+ }
65
+
66
+ let source: StackFrame | null = null;
67
+
68
+ if (
69
+ typeof stackFrame.lineNumber === 'number' &&
70
+ typeof stackFrame.columnNumber === 'number'
71
+ ) {
72
+ source = getSourceFromSourceMap(
73
+ bundleSourceMap,
74
+ stackFrame.lineNumber,
75
+ stackFrame.columnNumber,
76
+ );
77
+ }
78
+
79
+ if (!source?.fileName || !source.lineNumber) {
80
+ return getDisplayName(fiber.type);
81
+ }
82
+
83
+ if (!bundleSourceMap.sourcesContent) {
84
+ return getDisplayName(fiber.type);
85
+ }
86
+
87
+ const sourceIndex = bundleSourceMap.sources.indexOf(source.fileName);
88
+ if (sourceIndex === -1 || !bundleSourceMap.sourcesContent[sourceIndex]) {
89
+ return getDisplayName(fiber.type);
90
+ }
91
+
92
+ const sourceContent = bundleSourceMap.sourcesContent[sourceIndex];
93
+ const extractedName = extractComponentNameFromSource(
94
+ sourceContent,
95
+ source.lineNumber,
96
+ );
97
+
98
+ if (extractedName) {
99
+ return extractedName;
100
+ }
101
+
102
+ return getDisplayName(fiber.type);
103
+ };
@@ -0,0 +1,218 @@
1
+ import { Fiber } from '../types.js';
2
+
3
+ import { FiberSource } from './types.js';
4
+ import {
5
+ SCHEME_REGEX,
6
+ INTERNAL_SCHEME_PREFIXES,
7
+ ABOUT_REACT_PREFIX,
8
+ ANONYMOUS_FILE_PATTERNS,
9
+ SOURCE_FILE_EXTENSION_REGEX,
10
+ BUNDLED_FILE_PATTERN_REGEX,
11
+ QUERY_PARAMETER_PATTERN_REGEX,
12
+ } from './constants.js';
13
+ import { getOwnerStack } from './owner-stack.js';
14
+
15
+ export const hasDebugSource = (
16
+ fiber: Fiber,
17
+ ): fiber is Fiber & {
18
+ _debugSource: NonNullable<Fiber['_debugSource']>;
19
+ } => {
20
+ const debugSource = fiber._debugSource;
21
+ if (!debugSource) {
22
+ return false;
23
+ }
24
+ return (
25
+ typeof debugSource === 'object' &&
26
+ debugSource !== null &&
27
+ 'fileName' in debugSource &&
28
+ typeof debugSource.fileName === 'string' &&
29
+ 'lineNumber' in debugSource &&
30
+ typeof debugSource.lineNumber === 'number'
31
+ );
32
+ };
33
+
34
+ /**
35
+ * Returns the source of where the component is used. Available only in dev, for composite {@link Fiber}s.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * function Parent() {
40
+ * const data = useData();
41
+ * return <Child name={data.name} />; // <-- captures THIS line
42
+ * }
43
+ *
44
+ * function Child({ name }) {
45
+ * return <div>{name}</div>;
46
+ * }
47
+ *
48
+ * const source = await getSource(fiber);
49
+ * console.log(source.fileName, source.lineNumber);
50
+ * ```
51
+ */
52
+ export const getSource = async (
53
+ fiber: Fiber,
54
+ cache = true,
55
+ fetchFn?: (url: string) => Promise<Response>,
56
+ ): Promise<FiberSource | null> => {
57
+ if (hasDebugSource(fiber)) {
58
+ const debugSource = fiber._debugSource;
59
+ return debugSource || null;
60
+ }
61
+
62
+ const componentStack = await getOwnerStack(fiber, cache, fetchFn);
63
+
64
+ for (const stackFrame of componentStack) {
65
+ if (stackFrame.fileName) {
66
+ return {
67
+ fileName: stackFrame.fileName,
68
+ lineNumber: stackFrame.lineNumber,
69
+ columnNumber: stackFrame.columnNumber,
70
+ functionName: stackFrame.functionName,
71
+ };
72
+ }
73
+ }
74
+ return null;
75
+ };
76
+
77
+ const getPathSegmentCount = (path: string): number =>
78
+ path.split('/').filter(Boolean).length;
79
+
80
+ const getFirstPathSegment = (path: string): string | null => {
81
+ const segments = path.split('/').filter(Boolean);
82
+ return segments[0] ?? null;
83
+ };
84
+
85
+ const stripSingleBasePathPrefix = (path: string): string => {
86
+ const firstSlashIndex = path.indexOf('/', 1);
87
+ if (firstSlashIndex === -1) {
88
+ return path;
89
+ }
90
+
91
+ const basePath = path.slice(0, firstSlashIndex);
92
+ if (getPathSegmentCount(basePath) !== 1) {
93
+ return path;
94
+ }
95
+
96
+ const remainderPath = path.slice(firstSlashIndex);
97
+ if (!SOURCE_FILE_EXTENSION_REGEX.test(remainderPath)) {
98
+ return path;
99
+ }
100
+
101
+ if (getPathSegmentCount(remainderPath) < 2) {
102
+ return path;
103
+ }
104
+
105
+ const firstRemainderSegment = getFirstPathSegment(remainderPath);
106
+ if (!firstRemainderSegment) {
107
+ return path;
108
+ }
109
+
110
+ if (firstRemainderSegment.startsWith('@')) {
111
+ return path;
112
+ }
113
+
114
+ if (firstRemainderSegment.length > 4) {
115
+ return path;
116
+ }
117
+
118
+ return remainderPath;
119
+ };
120
+
121
+ export const normalizeFileName = (fileName: string): string => {
122
+ if (!fileName) {
123
+ return '';
124
+ }
125
+
126
+ if (ANONYMOUS_FILE_PATTERNS.some((pattern) => pattern === fileName)) {
127
+ return '';
128
+ }
129
+
130
+ let normalizedFileName = fileName;
131
+
132
+ const isHttpUrl =
133
+ normalizedFileName.startsWith('http://') ||
134
+ normalizedFileName.startsWith('https://');
135
+ if (isHttpUrl) {
136
+ try {
137
+ const parsedUrl = new URL(normalizedFileName);
138
+ normalizedFileName = parsedUrl.pathname;
139
+ } catch {}
140
+ }
141
+
142
+ if (isHttpUrl) {
143
+ normalizedFileName = stripSingleBasePathPrefix(normalizedFileName);
144
+ }
145
+
146
+ if (normalizedFileName.startsWith(ABOUT_REACT_PREFIX)) {
147
+ const remainder = normalizedFileName.slice(ABOUT_REACT_PREFIX.length);
148
+ const slashIndex = remainder.indexOf('/');
149
+ const colonIndex = remainder.indexOf(':');
150
+
151
+ if (slashIndex !== -1 && (colonIndex === -1 || slashIndex < colonIndex)) {
152
+ normalizedFileName = remainder.slice(slashIndex + 1);
153
+ } else {
154
+ normalizedFileName = remainder;
155
+ }
156
+ }
157
+
158
+ let didStripPrefix = true;
159
+ while (didStripPrefix) {
160
+ didStripPrefix = false;
161
+ for (const prefix of INTERNAL_SCHEME_PREFIXES) {
162
+ if (normalizedFileName.startsWith(prefix)) {
163
+ normalizedFileName = normalizedFileName.slice(prefix.length);
164
+
165
+ if (prefix === 'file:///') {
166
+ normalizedFileName = `/${normalizedFileName.replace(/^\/+/, '')}`;
167
+ }
168
+
169
+ didStripPrefix = true;
170
+ break;
171
+ }
172
+ }
173
+ }
174
+
175
+ if (SCHEME_REGEX.test(normalizedFileName)) {
176
+ const schemeMatch = normalizedFileName.match(SCHEME_REGEX);
177
+ if (schemeMatch) {
178
+ normalizedFileName = normalizedFileName.slice(schemeMatch[0].length);
179
+ }
180
+ }
181
+
182
+ if (normalizedFileName.startsWith('//')) {
183
+ const firstPathSlashIndex = normalizedFileName.indexOf('/', 2);
184
+ normalizedFileName =
185
+ firstPathSlashIndex === -1
186
+ ? ''
187
+ : normalizedFileName.slice(firstPathSlashIndex);
188
+ }
189
+
190
+ const queryParameterIndex = normalizedFileName.indexOf('?');
191
+ if (queryParameterIndex !== -1) {
192
+ const potentialQueryParameters =
193
+ normalizedFileName.slice(queryParameterIndex);
194
+ if (QUERY_PARAMETER_PATTERN_REGEX.test(potentialQueryParameters)) {
195
+ normalizedFileName = normalizedFileName.slice(0, queryParameterIndex);
196
+ }
197
+ }
198
+
199
+ return normalizedFileName;
200
+ };
201
+
202
+ export const isSourceFile = (fileName: string): boolean => {
203
+ const normalizedFileName = normalizeFileName(fileName);
204
+
205
+ if (!normalizedFileName) {
206
+ return false;
207
+ }
208
+
209
+ if (!SOURCE_FILE_EXTENSION_REGEX.test(normalizedFileName)) {
210
+ return false;
211
+ }
212
+
213
+ if (BUNDLED_FILE_PATTERN_REGEX.test(normalizedFileName)) {
214
+ return false;
215
+ }
216
+
217
+ return true;
218
+ };
@@ -0,0 +1,6 @@
1
+ export * from './owner-stack.js';
2
+ export * from './get-source.js';
3
+ export * from './symbolication.js';
4
+ export * from './types.js';
5
+ export * from './parse-stack.js';
6
+ export * from './get-display-name-from-source.js';