0
# Syntax Detection
1
2
Utilities for detecting ECMAScript module syntax, CommonJS syntax, and validating whether imports are safe for dynamic import() calls in Node.js. Uses heuristic-based pattern matching for fast analysis.
3
4
## Capabilities
5
6
### Has ESM Syntax
7
8
Determines if a given code string contains ECMAScript module syntax.
9
10
```typescript { .api }
11
/**
12
* Determines if a given code string contains ECMAScript module syntax
13
* @param code - The source code to analyse
14
* @param opts - Options for syntax detection
15
* @returns `true` if the code contains ESM syntax, otherwise `false`
16
*/
17
function hasESMSyntax(code: string, opts?: DetectSyntaxOptions): boolean;
18
```
19
20
**Usage Examples:**
21
22
```typescript
23
import { hasESMSyntax } from "mlly";
24
25
console.log(hasESMSyntax("export default foo = 123")); // true
26
console.log(hasESMSyntax("import { bar } from 'baz'")); // true
27
console.log(hasESMSyntax("const x = require('y')")); // false
28
```
29
30
### Has CJS Syntax
31
32
Determines if a given string of code contains CommonJS syntax.
33
34
```typescript { .api }
35
/**
36
* Determines if a given string of code contains CommonJS syntax
37
* @param code - The source code to analyse
38
* @param opts - Options for syntax detection
39
* @returns `true` if the code contains CommonJS syntax, `false` otherwise
40
*/
41
function hasCJSSyntax(code: string, opts?: DetectSyntaxOptions): boolean;
42
```
43
44
**Usage Examples:**
45
46
```typescript
47
import { hasCJSSyntax } from "mlly";
48
49
console.log(hasCJSSyntax("module.exports = {}")); // true
50
console.log(hasCJSSyntax("exports.foo = 'bar'")); // true
51
console.log(hasCJSSyntax("const x = require('y')")); // true
52
console.log(hasCJSSyntax("export default foo")); // false
53
```
54
55
### Detect Syntax
56
57
Analyses the supplied code to determine if it contains ECMAScript module syntax, CommonJS syntax, or both.
58
59
```typescript { .api }
60
/**
61
* Analyses the supplied code to determine if it contains ECMAScript module syntax, CommonJS syntax, or both
62
* @param code - The source code to analyse
63
* @param opts - Options for syntax detection
64
* @returns An object indicating the presence of ESM syntax (`hasESM`), CJS syntax (`hasCJS`) and whether both syntaxes are present (`isMixed`)
65
*/
66
function detectSyntax(code: string, opts?: DetectSyntaxOptions): {
67
hasESM: boolean;
68
hasCJS: boolean;
69
isMixed: boolean;
70
};
71
```
72
73
**Usage Example:**
74
75
```typescript
76
import { detectSyntax } from "mlly";
77
78
// Mixed syntax (common in legacy packages)
79
const result = detectSyntax('export default require("lodash")');
80
console.log(result); // { hasESM: true, hasCJS: true, isMixed: true }
81
82
// Pure ESM
83
const esmResult = detectSyntax('export const foo = "bar"');
84
console.log(esmResult); // { hasESM: true, hasCJS: false, isMixed: false }
85
```
86
87
### Is Valid Node Import
88
89
Validates whether a given identifier represents a valid node import, based on its protocol, file extension, and optionally its contents.
90
91
```typescript { .api }
92
/**
93
* Validates whether a given identifier represents a valid node import, based on its protocol, file extension, and optionally its contents
94
* @param id - The identifier or URL of the import to validate
95
* @param options - Options for resolving and validating the import
96
* @returns A promise that resolves to `true` if the import is valid, otherwise `false`
97
*/
98
function isValidNodeImport(id: string, options?: ValidNodeImportOptions): Promise<boolean>;
99
```
100
101
**Usage Examples:**
102
103
```typescript
104
import { isValidNodeImport } from "mlly";
105
106
// Check if safe to use dynamic import()
107
const isValid = await isValidNodeImport("some-package");
108
if (isValid) {
109
const module = await import("some-package");
110
} else {
111
// Need to use CommonJS require or bundler transform
112
console.log("This import requires special handling");
113
}
114
115
// Built-in modules are always valid
116
console.log(await isValidNodeImport("fs")); // true
117
console.log(await isValidNodeImport("node:path")); // true
118
119
// Data URLs are valid
120
console.log(await isValidNodeImport("data:text/javascript,export default 42")); // true
121
```
122
123
**Algorithm:**
124
125
The `isValidNodeImport` function uses the following validation steps:
126
127
1. **Protocol Check** - If is `data:` return `true` (✅ valid) - If is not `node:`, `file:` or `data:`, return `false` (❌ invalid)
128
2. **Resolve Path** - Use Node.js Resolution algorithm to resolve full path
129
3. **Extension Check** - If is `.mjs`, `.cjs`, `.node` or `.wasm`, return `true` (✅ valid) - If is not `.js`, return `false` (❌ invalid) - If matches known mixed syntax patterns (`.esm.js`, `.es.js`, etc) return `false` (❌ invalid)
130
4. **Package.json Check** - Read closest `package.json` file - If `type: 'module'` field is set, return `true` (✅ valid)
131
5. **Source Analysis** - Read source code of resolved path - Try to detect CommonJS syntax usage - If yes, return `true` (✅ valid) - Try to detect ESM syntax usage - If yes, return `false` (❌ invalid)
132
133
## Types
134
135
```typescript { .api }
136
interface DetectSyntaxOptions {
137
/** Indicates whether comments should be stripped from the code before syntax checking */
138
stripComments?: boolean;
139
}
140
141
interface ValidNodeImportOptions extends ResolveOptions {
142
/** The contents of the import, which may be analyzed to see if it contains CJS or ESM syntax as a last step in checking whether it is a valid import */
143
code?: string;
144
/** Protocols that are allowed as valid node imports */
145
allowedProtocols?: Array<string>;
146
/** Whether to strip comments from the code before checking for ESM syntax */
147
stripComments?: boolean;
148
}
149
```