Skip to content

Commit

Permalink
feat(feature-detector): introduces findDependencies() which returns t…
Browse files Browse the repository at this point in the history
…he list of globals.
  • Loading branch information
feugy committed Jan 12, 2023
1 parent 52b9bfe commit b709b27
Show file tree
Hide file tree
Showing 13 changed files with 2,358 additions and 18,272 deletions.
27 changes: 24 additions & 3 deletions docs/pages/packages/feature-detector.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,45 @@ This package includes built-in TypeScript support.

## Usage

Here is an example of checking whether a file's default export is a function matching the edge signature, such as:
Here is a test file. It's in JavaScript, but TypeScript is supported as well.

```js
// test.js
if (typeof process !== 'undefined') {
console.log('in node', __dirname)
} else if (typeof window !== 'undefined') {
console.log('in a browser', document)
}
setTimeout(() => console.log('setTimeout is not a global, nor console'), 0)

export default function () {
return Response.json({ message: 'hello world!' })
}
```

In the code snippet bellow, we're checking:

1. whether this file is a function matching the edge signature
2. globals used, that are not provided by the Edge Runtime

```ts
import { hasEdgeSignature } from '@edge-runtime/feature-detector'
import {
hasEdgeSignature,
findDependencies,
} from '@edge-runtime/feature-detector'

const sourceFilePath = './test.js' // could be TypeScript as well. Must be in current working directory

// 1
if (hasEdgeSignature(sourceFilePath)) {
console.log(`${sourcefilePath} can run on the edge``)
console.log(`${sourcefilePath} can run on the edge`)
}
// 2
console.log(findDependencies(sourceFilePath).globals)
```

Z

## API

<Callout type='warning' emoji='⚠️'>
Expand Down
5 changes: 4 additions & 1 deletion packages/feature-detector/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@
"ts-morph": "17.0.1"
},
"devDependencies": {
"axios": "0.27.2",
"jest-text-transformer": "1.0.4",
"next": "13",
"react": "18",
"react-dom": "18",
"tsup": "6"
},
"peerDependencies": {
"esbuild": "^0.16.16"
},
"engines": {
"node": ">=14"
},
Expand All @@ -42,7 +46,6 @@
"scripts": {
"build": "tsup",
"clean:build": "rm -rf dist",
"patch-type-definition": "ts-node scripts/patch-type-definition.ts",
"prebuild": "pnpm run clean:build",
"test": "jest"
},
Expand Down
31 changes: 31 additions & 0 deletions packages/feature-detector/scripts/build-fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// run with ts-node
import { build } from 'esbuild'
import { readdir, readFile, writeFile } from 'node:fs/promises'
import { join } from 'node:path'

const fixtureFolder = join(__dirname, '../test/fixtures')
const resultExtension = '.out.js'

async function transpileAll() {
for (const file of await readdir(fixtureFolder)) {
if (!file.endsWith(resultExtension)) {
await transpileFile(file)
}
}
}

async function transpileFile(file: string) {
const filePath = join(fixtureFolder, file)
await build({
entryPoints: [filePath],
bundle: true,
outfile: filePath.replace(/\..+$/, resultExtension),
})
}

transpileAll()
.then(() => process.exit(0))
.catch((error) => {
console.log('Errored', error)
process.exit(1)
})
107 changes: 0 additions & 107 deletions packages/feature-detector/scripts/patch-type-definition.ts

This file was deleted.

37 changes: 37 additions & 0 deletions packages/feature-detector/src/find-dependencies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Project, ts } from 'ts-morph'
import { buildProject } from './utils/project'

/**
* Find the list of globals used by source files in the provided project.
* Analyzed source files can be filtered by provided a list of glob patterns (default to all TypeScript and JavaScript files, excluding type definitions)
*/
export function findDependencies(
sourcePath: string,
project: Project = buildProject()
): {
globals: string[]
} {
const globals = new Set<string>()
const sourceFile = project.addSourceFileAtPath(sourcePath)
const program = project.getProgram().compilerObject
const diagnostics = program.getSemanticDiagnostics(sourceFile.compilerNode)
for (const { code, messageText } of diagnostics) {
if (
// see all messages: https://github.com/microsoft/TypeScript/blob/main/src/compiler/diagnosticMessages.json#L1631
code === 2304 ||
code === 2311 ||
code === 2552 ||
(code >= 2562 && code <= 2563) ||
(code >= 2580 && code <= 2584) ||
(code >= 2591 && code <= 2593)
) {
const match = ts
.flattenDiagnosticMessageText(messageText, '\n')
.match(/^Cannot find name '([^']+)'\./)
if (match) {
globals.add(match[1])
}
}
}
return { globals: [...globals] }
}
1 change: 1 addition & 0 deletions packages/feature-detector/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './find-dependencies'
export * from './has-edge-signature'
4 changes: 3 additions & 1 deletion packages/feature-detector/src/utils/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import typeDefinitionContent from './type-definition.txt'
export function buildProject() {
const project = new Project({
compilerOptions: {
types: [], // does not load node.js types to only rely on types provided by Edge runtime
allowJs: true,
checkJs: true,
},
})
project.createSourceFile('node_modules/index.d.ts', typeDefinitionContent)
project.addDirectoryAtPathIfExists('.')
project.addDirectoryAtPath('.')
return project
}
Loading

0 comments on commit b709b27

Please sign in to comment.