Skip to content

Commit b73e15d

Browse files
committed
feat: allow configuring transformation in more detail, add ecosystem tests
1 parent f3a1913 commit b73e15d

File tree

12 files changed

+641
-130
lines changed

12 files changed

+641
-130
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
"test:unit": "vitest run",
4747
"test:e2e": "playwright test",
4848
"test:types": "tsc --noEmit",
49-
"test:ecosystem": "vitest run test/transform/generate.test.ts && vitest run test/transform/generated-fixtures"
49+
"test:ecosystem": "vitest run test/transform/generate.test.ts && vitest run test/transform/generated-fixtures",
50+
"test:ecosystem:expanded": "vitest run test/transform/ecosystem-expanded.test.ts"
5051
},
5152
"dependencies": {
5253
"@babel/generator": "^7.23.0",
@@ -90,6 +91,7 @@
9091
"eslint": "8.36.0",
9192
"eslint-config-prettier": "latest",
9293
"eslint-plugin-prettier": "latest",
94+
"execa": "^8.0.1",
9395
"expect-type": "0.15.0",
9496
"jiti": "^1.18.2",
9597
"jsdom": "^22.1.0",

pnpm-lock.yaml

Lines changed: 34 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/types.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,27 @@ export type FlytrapConfig = {
9999
captureIgnores?: CaptureIgnore[]
100100
/**
101101
* Use Flytrap in environments with access to only browser APIs, eg. Cloudflare
102-
* Workers & Pages and Deno.
102+
* Workers & Pages and Deno Deploy.
103103
*/
104104
browser?: true
105+
transformOptions?: {
106+
/**
107+
* Allows you to define what code gets transformed by the Flytrap code transform and subsequently, what code data gets captured from. By default all code gets transformed and captured.
108+
*
109+
* By default, nothing is disabled.
110+
*/
111+
disableTransformation?: (
112+
| 'arrow-function'
113+
| 'function-declaration'
114+
| 'function-expression'
115+
| 'call-expression'
116+
)[]
117+
/**
118+
* What import format to use for the automatically injected Flytrap imports.
119+
* @default "esm"
120+
*/
121+
importFormat?: 'esm' | 'commonjs'
122+
}
105123
}
106124

107125
export type ErrorType = {

src/core/util.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,5 @@ export function err<T>(error: T) {
239239
error
240240
}
241241
}
242+
243+
export const extname = (filePath: string) => '.' + filePath.split('.').at(-1)

src/index.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,51 @@ import { createHumanLog } from './core/errors'
2222

2323
export function uff<T extends AnyFunction>(
2424
func: T,
25-
context: any = null,
25+
// context: any = null,
2626
id: string | null = null
2727
): T {
2828
return function (this: any) {
29-
// @ts-ignore
30-
// eslint-disable-next-line
31-
return func.apply(context ?? this, arguments)
29+
if (id) {
30+
addFunctionInvocation(id, {
31+
// eslint-disable-next-line
32+
args: Array.from(arguments),
33+
timestamp: Date.now()
34+
})
35+
}
36+
try {
37+
const context = null
38+
// @ts-ignore
39+
// eslint-disable-next-line
40+
const functionOutput = func.apply(context ?? this, arguments)
41+
if (id) {
42+
saveOutputForFunction(id, functionOutput)
43+
}
44+
return functionOutput
45+
} catch (error) {
46+
/**
47+
* Oops! We found a bug, let's send the current
48+
* executing function along with its data to the
49+
* Flytrap API.
50+
*/
51+
if (id) {
52+
saveErrorForFunction(id, error)
53+
log.info('capture', `Captured error in async function with ID "${id}".`, { error })
54+
getFlytrapStorage()
55+
.saveCapture(_executingFunctions, _functionCalls, error as Error)
56+
.catch((saveError) => {
57+
console.error(
58+
createHumanLog({
59+
events: ['capture_failed'],
60+
explanations: ['api_capture_error_response'],
61+
solutions: ['try_again_contact_us']
62+
}).toString()
63+
)
64+
console.error(saveError)
65+
})
66+
}
67+
68+
throw error
69+
}
3270
} as T
3371
}
3472

src/transform.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ import { UnpluginOptions, createUnplugin } from 'unplugin'
33
import { parseURL, parseQuery } from 'ufo'
44
import MagicString from 'magic-string'
55
import { addFlytrapInit, addMissingFlytrapImports } from './transform/imports'
6-
import { flytrapTransformArtifacts } from './transform/index'
6+
import { flytrapTransformArtifacts, flytrapTransformUff } from './transform/index'
77
import { packageDirectorySync } from 'pkg-dir'
88
import { loadConfig } from './transform/config'
99
import { setFlytrapConfig } from './core/config'
1010
import { log } from './core/logging'
11-
import { normalizeFilepath, tryCatchSync } from './core/util'
11+
import { empty, normalizeFilepath, tryCatchSync } from './core/util'
1212
import { readFileSync } from 'node:fs'
1313
import { excludeDirectoriesIncludeFilePath } from './transform/excludes'
1414
import { containsScriptTags, parseScriptTags } from './transform/parseScriptTags'
1515
import { calculateSHA256Checksum, getFileExtension } from './transform/util'
1616
import { upsertArtifacts } from './transform/artifacts/cache'
17-
import { Artifact, encrypt } from './exports'
17+
import { Artifact, FlytrapConfig, encrypt } from './exports'
1818
import { createHumanLog } from './core/errors'
1919

2020
const transformedFiles = new Set<string>([])
@@ -47,7 +47,7 @@ export const unpluginOptions: UnpluginOptions = {
4747

4848
return false
4949
},
50-
async transform(code, id) {
50+
async transform(code, id, config?: FlytrapConfig) {
5151
if (
5252
code.includes('@flytrap-ignore') ||
5353
id.includes('/node_modules/') ||
@@ -72,7 +72,10 @@ export const unpluginOptions: UnpluginOptions = {
7272
}
7373

7474
// Logging config
75-
const config = await loadConfig()
75+
if (!config) {
76+
const loadedConfig = await loadConfig()
77+
config = loadedConfig
78+
}
7679
if (config) setFlytrapConfig(config)
7780

7881
// Exclude directories
@@ -112,7 +115,7 @@ export const unpluginOptions: UnpluginOptions = {
112115
: new MagicString(code)
113116

114117
// add missing Flytrap imports
115-
addMissingFlytrapImports(ss, id, config?.browser)
118+
addMissingFlytrapImports(ss, id, config)
116119

117120
// add Flytrap init
118121
if (process.env.NODE_ENV !== 'test') {
@@ -154,7 +157,8 @@ export const unpluginOptions: UnpluginOptions = {
154157
}
155158

156159
transformedFiles.add(id)
157-
return flytrapTransformArtifacts(ss.toString(), normalizeFilepath(pkgDirPath, id), config)
160+
return flytrapTransformUff(ss.toString(), normalizeFilepath(pkgDirPath, id), config)
161+
// return flytrapTransformArtifacts(ss.toString(), normalizeFilepath(pkgDirPath, id), config)
158162
} catch (e) {
159163
if (process.env.NODE_ENV === 'test') {
160164
throw e
@@ -174,14 +178,15 @@ export const unpluginOptions: UnpluginOptions = {
174178
throw log.toString()
175179
}
176180

177-
if (!config.projectId || !config.secretApiKey) {
181+
if (!config.projectId || !config.secretApiKey || empty(config.projectId, config.secretApiKey)) {
178182
const log = createHumanLog({
179183
events: ['transform_failed'],
180184
explanations: ['invalid_config'],
181185
solutions: ['configuration_fix']
182186
})
183187
throw log.toString()
184188
}
189+
185190
// Find package root
186191
const pkgDirPath = packageDirectorySync()
187192
if (!pkgDirPath) {

src/transform/imports.ts

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import { FLYTRAP_PACKAGE_NAME } from '../core/config'
44
import { FlytrapConfig } from '../core/types'
55
import * as flytrapExports from '../index'
66
import { parseCode } from './parser'
7+
import { extname } from '../core/util'
78

89
export function getRequiredExportsForCapture(): string[] {
9-
return ['useFlytrapCall', 'useFlytrapCallAsync', 'useFlytrapFunction', 'setFlytrapConfig']
10+
// return ['useFlytrapCall', 'useFlytrapCallAsync', 'useFlytrapFunction', 'setFlytrapConfig', 'uff']
11+
return ['uff']
1012
}
1113

1214
export function getCoreExports(): string[] {
@@ -32,7 +34,11 @@ export function findStartingIndex(s: MagicString, fileNamePath?: string) {
3234
return 0
3335
}
3436

35-
export function addMissingFlytrapImports(s: MagicString, fileNamePath: string, browser = false) {
37+
export function addMissingFlytrapImports(
38+
s: MagicString,
39+
fileNamePath: string,
40+
config?: Partial<FlytrapConfig>
41+
) {
3642
const statements = findStaticImports(s.toString()).filter(
3743
(i) => i.specifier === FLYTRAP_PACKAGE_NAME || i.specifier === FLYTRAP_PACKAGE_NAME + '/browser'
3844
)
@@ -47,12 +53,29 @@ export function addMissingFlytrapImports(s: MagicString, fileNamePath: string, b
4753

4854
if (importsToBeAdded.length > 0) {
4955
const startingIndex = findStartingIndex(s, fileNamePath)
50-
s.appendLeft(
51-
startingIndex,
52-
`\n\nimport { ${importsToBeAdded.join(', ')} } from '${FLYTRAP_PACKAGE_NAME}${
53-
browser ? '/browser' : ''
54-
}';\n\n`
55-
)
56+
57+
let useCommonjsImportSyntax = config?.transformOptions?.importFormat === 'commonjs'
58+
if (extname(fileNamePath) === '.cjs') {
59+
useCommonjsImportSyntax = true
60+
}
61+
if (extname(fileNamePath) === '.mjs') {
62+
useCommonjsImportSyntax = false
63+
}
64+
if (useCommonjsImportSyntax) {
65+
s.appendLeft(
66+
startingIndex,
67+
`\n\nconst { ${importsToBeAdded.join(', ')} } = require('${FLYTRAP_PACKAGE_NAME}${
68+
config?.browser === true ? '/browser' : ''
69+
}');\n\n`
70+
)
71+
} else {
72+
s.appendLeft(
73+
startingIndex,
74+
`\n\nimport { ${importsToBeAdded.join(', ')} } from '${FLYTRAP_PACKAGE_NAME}${
75+
config?.browser === true ? '/browser' : ''
76+
}';\n\n`
77+
)
78+
}
5679
}
5780

5881
return s

0 commit comments

Comments
 (0)