oxc-parser 0.38.0 → 0.40.0

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/README.md CHANGED
@@ -1,21 +1,51 @@
1
- # The JavaScript Oxidation Compiler
1
+ # Oxc Parser
2
2
 
3
- See `index.d.ts` for `parseSync` and `parseAsync` API.
3
+ ## Features
4
+
5
+ - Returns ESM information.
6
+ - Built-in `magic-string` on the Rust side exposed through N-API.
7
+ - "clever" approach to overcome the Rust UTF8 vs JavaScript UTF16 length problem.
8
+
9
+ ## Caveat
10
+
11
+ The parser alone does not fully check for syntax errors that are associated with semantic data (symbols and scopes).
12
+ The full compiler is needed for such case, as the compiler does an additional semantic pass.
13
+
14
+ With this caveat, `oxc-parser` is best suited for parser plugins,
15
+ where you need quick access to ESM information, as well as fast `magic-string` operations.
16
+
17
+ ## API
4
18
 
5
19
  ```javascript
6
- import assert from 'assert';
7
- import oxc from 'oxc-parser';
20
+ import oxc from './index.js';
21
+
22
+ // The emoji makes the span of `import.meta.url` to be different in UTF8 and UTF16.
23
+ const code = 'const url: String = /* 🤨 */ import.meta.url;';
24
+
25
+ // File extension is used to determine which dialect to parse source as.
26
+ const filename = 'test.tsx';
27
+
28
+ const result = oxc.parseSync(filename, code);
29
+ // or `await oxc.parseAsync(filename, code)`
30
+
31
+ // An array of errors, if any.
32
+ console.log(result.errors);
33
+
34
+ // AST and comments.
35
+ console.log(result.program, result.comments);
8
36
 
9
- const sourceText = "let foo: Foo = 'foo';";
10
- // Filename extension is used to determine which
11
- // dialect to parse source as
12
- const options = { sourceFilename: 'text.tsx' };
37
+ // ESM information - imports, exports, `import.meta`s.
38
+ console.log(result.module);
13
39
 
14
- test(oxc.parseSync(sourceText, options));
15
- test(await oxc.parseAsync(sourceText, options));
40
+ // A `magic-string` instance for accessing and manipulating the source text.
41
+ // All returned spans are in UTF8 offsets, which cannot be used directly on our JavaScript.
42
+ // JavaScript string lengths are in UTF16 offsets.
43
+ const ms = result.magicString;
16
44
 
17
- function test(ret) {
18
- assert(ret.program.body.length == 1);
19
- assert(ret.errors.length == 0);
45
+ for (const span of result.module.importMetas) {
46
+ // Extra methods for access the source text through spans with UTF8 offsets.
47
+ console.log(ms.getSourceText(span.start, span.end)); // prints `import.meta`
48
+ console.log(ms.getLineColumnNumber(span.start)); // prints `{ line: 0, column: 20 }`
49
+ console.log(code.substring(ms.getUtf16ByteOffset(span.start)).startsWith('import.meta.url')); // prints `true`
20
50
  }
21
51
  ```
package/bindings.js CHANGED
@@ -361,8 +361,13 @@ if (!nativeBinding) {
361
361
  throw new Error(`Failed to load native binding`)
362
362
  }
363
363
 
364
- module.exports.moduleLexerAsync = nativeBinding.moduleLexerAsync
365
- module.exports.moduleLexerSync = nativeBinding.moduleLexerSync
364
+ module.exports.MagicString = nativeBinding.MagicString
365
+ module.exports.ParseResult = nativeBinding.ParseResult
366
+ module.exports.ExportExportNameKind = nativeBinding.ExportExportNameKind
367
+ module.exports.ExportImportNameKind = nativeBinding.ExportImportNameKind
368
+ module.exports.ExportLocalNameKind = nativeBinding.ExportLocalNameKind
369
+ module.exports.ImportNameKind = nativeBinding.ImportNameKind
366
370
  module.exports.parseAsync = nativeBinding.parseAsync
367
371
  module.exports.parseSync = nativeBinding.parseSync
368
372
  module.exports.parseWithoutReturn = nativeBinding.parseWithoutReturn
373
+ module.exports.Severity = nativeBinding.Severity
package/index.d.ts CHANGED
@@ -2,6 +2,34 @@
2
2
  /* eslint-disable */
3
3
 
4
4
  export * from '@oxc-project/types';
5
+ export declare class MagicString {
6
+ /** Get source text from utf8 offset. */
7
+ getSourceText(start: number, end: number): string
8
+ /** Get 0-based line and column number from utf8 offset. */
9
+ getLineColumnNumber(offset: number): LineColumn
10
+ /** Get UTF16 byte offset from UTF8 byte offset. */
11
+ getUtf16ByteOffset(offset: number): number
12
+ length(): number
13
+ toString(): string
14
+ append(input: string): this
15
+ appendLeft(index: number, input: string): this
16
+ appendRight(index: number, input: string): this
17
+ indent(): this
18
+ prepend(input: string): this
19
+ prependLeft(index: number, input: string): this
20
+ prependRight(index: number, input: string): this
21
+ relocate(start: number, end: number, to: number): this
22
+ remove(start: number, end: number): this
23
+ }
24
+
25
+ export declare class ParseResult {
26
+ get program(): import("@oxc-project/types").Program
27
+ get module(): EcmaScriptModule
28
+ get comments(): Array<Comment>
29
+ get errors(): Array<Error>
30
+ get magicString(): MagicString
31
+ }
32
+
5
33
  export interface Comment {
6
34
  type: 'Line' | 'Block'
7
35
  value: string
@@ -9,104 +37,125 @@ export interface Comment {
9
37
  end: number
10
38
  }
11
39
 
12
- export interface ModuleLexer {
13
- imports: Array<ModuleLexerImportSpecifier>
14
- exports: Array<ModuleLexerExportSpecifier>
40
+ export interface EcmaScriptModule {
15
41
  /**
16
- * ESM syntax detection
42
+ * Has ESM syntax.
43
+ *
44
+ * i.e. `import` and `export` statements, and `import.meta`.
17
45
  *
18
- * The use of ESM syntax: import / export statements and `import.meta`
46
+ * Dynamic imports `import('foo')` are ignored since they can be used in non-ESM files.
19
47
  */
20
48
  hasModuleSyntax: boolean
21
- /** Facade modules that only use import / export syntax */
22
- facade: boolean
49
+ /** Import Statements. */
50
+ staticImports: Array<StaticImport>
51
+ /** Export Statements. */
52
+ staticExports: Array<StaticExport>
53
+ /** Span positions` of `import.meta` */
54
+ importMetas: Array<Span>
23
55
  }
24
56
 
25
- /**
26
- * # Panics
27
- *
28
- * * Tokio crashes
29
- */
30
- export declare function moduleLexerAsync(sourceText: string, options?: ParserOptions | undefined | null): Promise<ModuleLexer>
31
-
32
- export interface ModuleLexerExportSpecifier {
33
- /** Exported name */
34
- n: string
35
- /** Local name, or undefined. */
36
- ln?: string
37
- /** Start of exported name */
38
- s: number
39
- /** End of exported name */
40
- e: number
41
- /** Start of local name */
42
- ls?: number
43
- /** End of local name */
44
- le?: number
45
- }
46
-
47
- export interface ModuleLexerImportSpecifier {
48
- /**
49
- * Module name
50
- *
51
- * To handle escape sequences in specifier strings, the .n field of imported specifiers will be provided where possible.
52
- *
53
- * For dynamic import expressions, this field will be empty if not a valid JS string.
54
- */
55
- n?: string
56
- /** Start of module specifier */
57
- s: number
58
- /** End of module specifier */
59
- e: number
60
- /** Start of import statement */
61
- ss: number
62
- /** End of import statement */
63
- se: number
64
- /**
65
- * Import Type
66
- * * If this import keyword is a dynamic import, this is the start value.
67
- * * If this import keyword is a static import, this is -1.
68
- * * If this import keyword is an import.meta expression, this is -2.
69
- * * If this import is an `export *`, this is -3.
70
- */
71
- d: number
57
+ export interface Error {
58
+ severity: Severity
59
+ message: string
60
+ labels: Array<ErrorLabel>
61
+ helpMessage?: string
62
+ }
63
+
64
+ export interface ErrorLabel {
65
+ message?: string
66
+ start: number
67
+ end: number
68
+ }
69
+
70
+ export interface ExportExportName {
71
+ kind: ExportExportNameKind
72
+ name?: string
73
+ start?: number
74
+ end?: number
75
+ }
76
+
77
+ export declare const enum ExportExportNameKind {
78
+ /** `export { name } */
79
+ Name = 'Name',
80
+ /** `export default expression` */
81
+ Default = 'Default',
82
+ /** `export * from "mod" */
83
+ None = 'None'
84
+ }
85
+
86
+ export interface ExportImportName {
87
+ kind: ExportImportNameKind
88
+ name?: string
89
+ start?: number
90
+ end?: number
91
+ }
92
+
93
+ export declare const enum ExportImportNameKind {
94
+ /** `export { name } */
95
+ Name = 'Name',
96
+ /** `export * as ns from "mod"` */
97
+ All = 'All',
98
+ /** `export * from "mod"` */
99
+ AllButDefault = 'AllButDefault',
100
+ /** Does not have a specifier. */
101
+ None = 'None'
102
+ }
103
+
104
+ export interface ExportLocalName {
105
+ kind: ExportLocalNameKind
106
+ name?: string
107
+ start?: number
108
+ end?: number
109
+ }
110
+
111
+ export declare const enum ExportLocalNameKind {
112
+ /** `export { name } */
113
+ Name = 'Name',
114
+ /** `export default expression` */
115
+ Default = 'Default',
72
116
  /**
73
- * If this import has an import assertion, this is the start value
74
- * Otherwise this is `-1`.
117
+ * If the exported value is not locally accessible from within the module.
118
+ * `export default function () {}`
75
119
  */
76
- a: number
120
+ None = 'None'
77
121
  }
78
122
 
79
- /**
80
- * Outputs the list of exports and locations of import specifiers,
81
- * including dynamic import and import meta handling.
82
- *
83
- * # Panics
84
- *
85
- * * File extension is invalid
86
- */
87
- export declare function moduleLexerSync(sourceText: string, options?: ParserOptions | undefined | null): ModuleLexer
123
+ export interface ImportName {
124
+ kind: ImportNameKind
125
+ name?: string
126
+ start?: number
127
+ end?: number
128
+ }
88
129
 
89
- /**
90
- * # Panics
91
- *
92
- * * Tokio crashes
93
- */
94
- export declare function parseAsync(sourceText: string, options?: ParserOptions | undefined | null): Promise<ParseResult>
130
+ export declare const enum ImportNameKind {
131
+ /** `import { x } from "mod"` */
132
+ Name = 'Name',
133
+ /** `import * as ns from "mod"` */
134
+ NamespaceObject = 'NamespaceObject',
135
+ /** `import defaultExport from "mod"` */
136
+ Default = 'Default'
137
+ }
95
138
 
96
- export interface ParseResult {
97
- program: import("@oxc-project/types").Program
98
- comments: Array<Comment>
99
- errors: Array<string>
139
+ export interface LineColumn {
140
+ line: number
141
+ column: number
142
+ }
143
+
144
+ export interface OverwriteOptions {
145
+ contentOnly: boolean
100
146
  }
101
147
 
102
148
  /**
103
- * Babel Parser Options
149
+ * Parse asynchronously.
104
150
  *
105
- * <https://github.com/babel/babel/blob/v7.26.2/packages/babel-parser/typings/babel-parser.d.ts>
151
+ * Note: This function can be slower than `parseSync` due to the overhead of spawning a thread.
106
152
  */
153
+ export declare function parseAsync(filename: string, sourceText: string, options?: ParserOptions | undefined | null): Promise<ParseResult>
154
+
107
155
  export interface ParserOptions {
108
156
  sourceType?: 'script' | 'module' | 'unambiguous' | undefined
109
- sourceFilename?: string
157
+ /** Treat the source text as `js`, `jsx`, `ts`, or `tsx`. */
158
+ lang?: 'js' | 'jsx' | 'ts' | 'tsx'
110
159
  /**
111
160
  * Emit `ParenthesizedExpression` in AST.
112
161
  *
@@ -119,22 +168,110 @@ export interface ParserOptions {
119
168
  preserveParens?: boolean
120
169
  }
121
170
 
122
- /**
123
- * # Panics
124
- *
125
- * * File extension is invalid
126
- * * Serde JSON serialization
127
- */
128
- export declare function parseSync(sourceText: string, options?: ParserOptions | undefined | null): ParseResult
171
+ /** Parse synchronously. */
172
+ export declare function parseSync(filename: string, sourceText: string, options?: ParserOptions | undefined | null): ParseResult
129
173
 
130
174
  /**
131
175
  * Parse without returning anything.
132
- * This is for benchmark purposes such as measuring napi communication overhead.
133
- *
134
- * # Panics
135
176
  *
136
- * * File extension is invalid
137
- * * Serde JSON serialization
177
+ * This is for benchmark purposes such as measuring napi communication overhead.
138
178
  */
139
- export declare function parseWithoutReturn(sourceText: string, options?: ParserOptions | undefined | null): void
179
+ export declare function parseWithoutReturn(filename: string, sourceText: string, options?: ParserOptions | undefined | null): void
180
+
181
+ export declare const enum Severity {
182
+ Error = 'Error',
183
+ Warning = 'Warning',
184
+ Advice = 'Advice'
185
+ }
186
+
187
+ export interface SourceMapOptions {
188
+ includeContent?: boolean
189
+ source?: string
190
+ hires?: boolean
191
+ }
192
+
193
+ export interface Span {
194
+ start: number
195
+ end: number
196
+ }
197
+
198
+ export interface StaticExport {
199
+ start: number
200
+ end: number
201
+ entries: Array<StaticExportEntry>
202
+ }
203
+
204
+ export interface StaticExportEntry {
205
+ start: number
206
+ end: number
207
+ moduleRequest?: ValueSpan
208
+ /** The name under which the desired binding is exported by the module`. */
209
+ importName: ExportImportName
210
+ /** The name used to export this binding by this module. */
211
+ exportName: ExportExportName
212
+ /** The name that is used to locally access the exported value from within the importing module. */
213
+ localName: ExportLocalName
214
+ }
215
+
216
+ export interface StaticImport {
217
+ /** Start of import statement. */
218
+ start: number
219
+ /** End of import statement. */
220
+ end: number
221
+ /**
222
+ * Import source.
223
+ *
224
+ * ```js
225
+ * import { foo } from "mod";
226
+ * // ^^^
227
+ * ```
228
+ */
229
+ moduleRequest: ValueSpan
230
+ /**
231
+ * Import specifiers.
232
+ *
233
+ * Empty for `import "mod"`.
234
+ */
235
+ entries: Array<StaticImportEntry>
236
+ }
237
+
238
+ export interface StaticImportEntry {
239
+ /**
240
+ * The name under which the desired binding is exported by the module.
241
+ *
242
+ * ```js
243
+ * import { foo } from "mod";
244
+ * // ^^^
245
+ * import { foo as bar } from "mod";
246
+ * // ^^^
247
+ * ```
248
+ */
249
+ importName: ImportName
250
+ /**
251
+ * The name that is used to locally access the imported value from within the importing module.
252
+ * ```js
253
+ * import { foo } from "mod";
254
+ * // ^^^
255
+ * import { foo as bar } from "mod";
256
+ * // ^^^
257
+ * ```
258
+ */
259
+ localName: ValueSpan
260
+ /**
261
+ * Whether this binding is for a TypeScript type-only import.
262
+ *
263
+ * `true` for the following imports:
264
+ * ```ts
265
+ * import type { foo } from "mod";
266
+ * import { type foo } from "mod";
267
+ * ```
268
+ */
269
+ isType: boolean
270
+ }
271
+
272
+ export interface ValueSpan {
273
+ value: string
274
+ start: number
275
+ end: number
276
+ }
140
277
 
package/index.js CHANGED
@@ -1,16 +1,44 @@
1
1
  const bindings = require('./bindings.js');
2
2
 
3
- module.exports.moduleLexerAsync = bindings.moduleLexerAsync;
4
- module.exports.moduleLexerSync = bindings.moduleLexerSync;
3
+ module.exports.MagicString = bindings.MagicString;
4
+ module.exports.ParseResult = bindings.ParseResult;
5
+ module.exports.ExportExportNameKind = bindings.ExportExportNameKind;
6
+ module.exports.ExportImportNameKind = bindings.ExportImportNameKind;
7
+ module.exports.ExportLocalNameKind = bindings.ExportLocalNameKind;
8
+ module.exports.ImportNameKind = bindings.ImportNameKind;
5
9
  module.exports.parseWithoutReturn = bindings.parseWithoutReturn;
10
+ module.exports.Severity = bindings.Severity;
11
+
12
+ function wrap(result) {
13
+ let program, module, comments, errors, magicString;
14
+ return {
15
+ get program() {
16
+ if (!program) program = JSON.parse(result.program);
17
+ return program;
18
+ },
19
+ get module() {
20
+ if (!module) module = result.module;
21
+ return module;
22
+ },
23
+ get comments() {
24
+ if (!comments) comments = result.comments;
25
+ return comments;
26
+ },
27
+ get errors() {
28
+ if (!errors) errors = result.errors;
29
+ return errors;
30
+ },
31
+ get magicString() {
32
+ if (!magicString) magicString = result.magicString;
33
+ return magicString;
34
+ },
35
+ };
36
+ }
6
37
 
7
38
  module.exports.parseAsync = async function parseAsync(...args) {
8
- const result = await bindings.parseAsync(...args);
9
- result.program = JSON.parse(result.program);
10
- return result;
39
+ return wrap(await bindings.parseAsync(...args));
11
40
  };
41
+
12
42
  module.exports.parseSync = function parseSync(...args) {
13
- const result = bindings.parseSync(...args);
14
- result.program = JSON.parse(result.program);
15
- return result;
43
+ return wrap(bindings.parseSync(...args));
16
44
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxc-parser",
3
- "version": "0.38.0",
3
+ "version": "0.40.0",
4
4
  "description": "Oxc Parser Node API",
5
5
  "keywords": [
6
6
  "Parser"
@@ -24,16 +24,16 @@
24
24
  "bindings.js"
25
25
  ],
26
26
  "dependencies": {
27
- "@oxc-project/types": "^0.38.0"
27
+ "@oxc-project/types": "^0.40.0"
28
28
  },
29
29
  "optionalDependencies": {
30
- "@oxc-parser/binding-win32-x64-msvc": "0.38.0",
31
- "@oxc-parser/binding-win32-arm64-msvc": "0.38.0",
32
- "@oxc-parser/binding-linux-x64-gnu": "0.38.0",
33
- "@oxc-parser/binding-linux-arm64-gnu": "0.38.0",
34
- "@oxc-parser/binding-linux-x64-musl": "0.38.0",
35
- "@oxc-parser/binding-linux-arm64-musl": "0.38.0",
36
- "@oxc-parser/binding-darwin-x64": "0.38.0",
37
- "@oxc-parser/binding-darwin-arm64": "0.38.0"
30
+ "@oxc-parser/binding-win32-x64-msvc": "0.40.0",
31
+ "@oxc-parser/binding-win32-arm64-msvc": "0.40.0",
32
+ "@oxc-parser/binding-linux-x64-gnu": "0.40.0",
33
+ "@oxc-parser/binding-linux-arm64-gnu": "0.40.0",
34
+ "@oxc-parser/binding-linux-x64-musl": "0.40.0",
35
+ "@oxc-parser/binding-linux-arm64-musl": "0.40.0",
36
+ "@oxc-parser/binding-darwin-x64": "0.40.0",
37
+ "@oxc-parser/binding-darwin-arm64": "0.40.0"
38
38
  }
39
39
  }