mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
feat(napi/parser): raw transfer support range field (#14319)
Raw transfer support `range` option, adding `range` field to every AST node.
This commit is contained in:
1 parent
c65e782092
commit
6374065095
15 files changed
+12919
-819
No files matched your search
@@ -68,7 +68,9 @@ src:
|
||||
- 'crates/oxc_traverse/src/generated/scopes_collector.rs'
|
||||
- 'napi/parser/generated/constants.js'
|
||||
- 'napi/parser/generated/deserialize/js.js'
|
||||
- 'napi/parser/generated/deserialize/js_range.js'
|
||||
- 'napi/parser/generated/deserialize/ts.js'
|
||||
- 'napi/parser/generated/deserialize/ts_range.js'
|
||||
- 'napi/parser/generated/lazy/constructors.js'
|
||||
- 'napi/parser/generated/lazy/types.js'
|
||||
- 'napi/parser/generated/lazy/walk.js'
|
||||
|
||||
@@ -119,6 +119,7 @@ impl ESTree for CatchParameterConverter<'_, '_> {
|
||||
const params = DESER[Vec<FormalParameter>](POS_OFFSET.items);
|
||||
if (uint32[(POS_OFFSET.rest) >> 2] !== 0 && uint32[(POS_OFFSET.rest + 4) >> 2] !== 0) {
|
||||
pos = uint32[(POS_OFFSET.rest) >> 2];
|
||||
let start, end;
|
||||
params.push({
|
||||
type: 'RestElement',
|
||||
...(IS_TS && { decorators: [] }),
|
||||
@@ -130,8 +131,9 @@ impl ESTree for CatchParameterConverter<'_, '_> {
|
||||
),
|
||||
value: null,
|
||||
}),
|
||||
start: DESER[u32]( POS_OFFSET<BindingRestElement>.span.start ),
|
||||
end: DESER[u32]( POS_OFFSET<BindingRestElement>.span.end ),
|
||||
start: start = DESER[u32]( POS_OFFSET<BindingRestElement>.span.start ),
|
||||
end: end = DESER[u32]( POS_OFFSET<BindingRestElement>.span.end ),
|
||||
...(RANGE && { range: [start, end] }),
|
||||
});
|
||||
}
|
||||
params
|
||||
@@ -192,6 +194,7 @@ impl ESTree for FormalParametersRest<'_, '_> {
|
||||
param.optional = DESER[bool](POS_OFFSET.pattern.optional);
|
||||
param.typeAnnotation = DESER[Option<Box<TSTypeAnnotation>>](POS_OFFSET.pattern.type_annotation);
|
||||
} else {
|
||||
let start, end;
|
||||
param = {
|
||||
type: 'TSParameterProperty',
|
||||
accessibility,
|
||||
@@ -200,8 +203,9 @@ impl ESTree for FormalParametersRest<'_, '_> {
|
||||
parameter: DESER[BindingPattern](POS_OFFSET.pattern),
|
||||
readonly,
|
||||
static: false,
|
||||
start: DESER[u32]( POS_OFFSET<BindingRestElement>.span.start ),
|
||||
end: DESER[u32]( POS_OFFSET<BindingRestElement>.span.end ),
|
||||
start: start = DESER[u32]( POS_OFFSET<BindingRestElement>.span.start ),
|
||||
end: end = DESER[u32]( POS_OFFSET<BindingRestElement>.span.end ),
|
||||
...(RANGE && { range: [start, end] }),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
@@ -416,6 +420,7 @@ impl ESTree for ArrowFunctionExpressionBody<'_> {
|
||||
}),
|
||||
start: THIS.start,
|
||||
end: THIS.end,
|
||||
...(RANGE && { range: [THIS.start, THIS.end] }),
|
||||
};
|
||||
value
|
||||
"
|
||||
@@ -455,11 +460,13 @@ impl ESTree for AssignmentTargetPropertyIdentifierInit<'_> {
|
||||
#[estree(raw_deser = "
|
||||
let node = DESER[Expression](POS_OFFSET.expression);
|
||||
if (preserveParens) {
|
||||
let start, end;
|
||||
node = {
|
||||
type: 'ParenthesizedExpression',
|
||||
expression: node,
|
||||
start: DESER[u32]( POS_OFFSET.span.start ),
|
||||
end: DESER[u32]( POS_OFFSET.span.end ),
|
||||
start: start = DESER[u32]( POS_OFFSET.span.start ),
|
||||
end: end = DESER[u32]( POS_OFFSET.span.end ),
|
||||
...(RANGE && { range: [start, end] }),
|
||||
};
|
||||
}
|
||||
node
|
||||
|
||||
@@ -56,7 +56,7 @@ impl ESTree for JSXOpeningElementSelfClosing<'_, '_> {
|
||||
ts_type = "JSXIdentifier",
|
||||
raw_deser = "
|
||||
const ident = DESER[Box<IdentifierReference>](POS);
|
||||
{ type: 'JSXIdentifier', name: ident.name, start: ident.start, end: ident.end }
|
||||
{ type: 'JSXIdentifier', name: ident.name, start: ident.start, end: ident.end, ...(RANGE && { range: ident.range }) }
|
||||
"
|
||||
)]
|
||||
pub struct JSXElementIdentifierReference<'a, 'b>(pub &'b IdentifierReference<'a>);
|
||||
@@ -75,7 +75,7 @@ impl ESTree for JSXElementIdentifierReference<'_, '_> {
|
||||
ts_type = "JSXIdentifier",
|
||||
raw_deser = "
|
||||
const thisExpr = DESER[Box<ThisExpression>](POS);
|
||||
{ type: 'JSXIdentifier', name: 'this', start: thisExpr.start, end: thisExpr.end }
|
||||
{ type: 'JSXIdentifier', name: 'this', start: thisExpr.start, end: thisExpr.end, ...(RANGE && { range: thisExpr.range }) }
|
||||
"
|
||||
)]
|
||||
pub struct JSXElementThisExpression<'b>(pub &'b ThisExpression);
|
||||
|
||||
@@ -155,6 +155,7 @@ impl Program<'_> {
|
||||
hashbang: DESER[Option<Hashbang>](POS_OFFSET.hashbang),
|
||||
start,
|
||||
end,
|
||||
...(RANGE && { range: [start, end] }),
|
||||
};
|
||||
program
|
||||
")]
|
||||
|
||||
@@ -60,27 +60,36 @@ impl ESTree for ExpressionStatementDirective<'_, '_> {
|
||||
if (body !== null && body.type === 'TSModuleDeclaration') {
|
||||
let innerId = body.id;
|
||||
if (innerId.type === 'Identifier') {
|
||||
let start, end;
|
||||
id = {
|
||||
type: 'TSQualifiedName',
|
||||
left: id,
|
||||
right: innerId,
|
||||
start: id.start,
|
||||
end: innerId.end,
|
||||
start: start = id.start,
|
||||
end: end = innerId.end,
|
||||
...(RANGE && { range: [start, end] }),
|
||||
};
|
||||
} else {
|
||||
// Replace `left` of innermost `TSQualifiedName` with a nested `TSQualifiedName` with `id` of
|
||||
// this module on left, and previous `left` of innermost `TSQualifiedName` on right
|
||||
while (true) {
|
||||
innerId.start = id.start;
|
||||
if (RANGE) {
|
||||
innerId.start = innerId.range[0] = id.start;
|
||||
} else {
|
||||
innerId.start = id.start;
|
||||
}
|
||||
if (innerId.left.type === 'Identifier') break;
|
||||
innerId = innerId.left;
|
||||
}
|
||||
|
||||
let start, end;
|
||||
innerId.left = {
|
||||
type: 'TSQualifiedName',
|
||||
left: id,
|
||||
right: innerId.left,
|
||||
start: id.start,
|
||||
end: innerId.left.end,
|
||||
start: start = id.start,
|
||||
end: end = innerId.left.end,
|
||||
...(RANGE && { range: [start, end] }),
|
||||
};
|
||||
id = body.id;
|
||||
}
|
||||
@@ -89,8 +98,18 @@ impl ESTree for ExpressionStatementDirective<'_, '_> {
|
||||
|
||||
// Skip `body` field if `null`
|
||||
const node = body === null
|
||||
? { type: 'TSModuleDeclaration', id, kind, declare, global, start, end }
|
||||
: { type: 'TSModuleDeclaration', id, body, kind, declare, global, start, end };
|
||||
? {
|
||||
type: 'TSModuleDeclaration',
|
||||
id, kind, declare, global,
|
||||
start, end,
|
||||
...(RANGE && { range: [start, end] })
|
||||
}
|
||||
: {
|
||||
type: 'TSModuleDeclaration',
|
||||
id, body, kind, declare, global,
|
||||
start, end,
|
||||
...(RANGE && { range: [start, end] })
|
||||
};
|
||||
node
|
||||
")]
|
||||
pub struct TSModuleDeclarationConverter<'a, 'b>(pub &'b TSModuleDeclaration<'a>);
|
||||
@@ -273,26 +292,30 @@ impl ESTree for TSMappedTypeConstraint<'_, '_> {
|
||||
let expression = DESER[TSTypeName](POS_OFFSET.expression);
|
||||
if (expression.type === 'TSQualifiedName') {
|
||||
let object = expression.left;
|
||||
let start, end;
|
||||
let parent = expression = {
|
||||
type: 'MemberExpression',
|
||||
object,
|
||||
property: expression.right,
|
||||
optional: false,
|
||||
computed: false,
|
||||
start: expression.start,
|
||||
end: expression.end,
|
||||
start: start = expression.start,
|
||||
end: end = expression.end,
|
||||
...(RANGE && { range: [start, end] }),
|
||||
};
|
||||
|
||||
while (object.type === 'TSQualifiedName') {
|
||||
const { left } = object;
|
||||
let start, end;
|
||||
parent = parent.object = {
|
||||
type: 'MemberExpression',
|
||||
object: left,
|
||||
property: object.right,
|
||||
optional: false,
|
||||
computed: false,
|
||||
start: object.start,
|
||||
end: object.end,
|
||||
start: start = object.start,
|
||||
end: end = object.end,
|
||||
...(RANGE && { range: [start, end] }),
|
||||
};
|
||||
object = left;
|
||||
}
|
||||
@@ -415,11 +438,13 @@ impl ESTree for TSFunctionTypeParams<'_, '_> {
|
||||
#[estree(raw_deser = "
|
||||
let node = DESER[TSType](POS_OFFSET.type_annotation);
|
||||
if (preserveParens) {
|
||||
let start, end;
|
||||
node = {
|
||||
type: 'TSParenthesizedType',
|
||||
typeAnnotation: node,
|
||||
start: DESER[u32]( POS_OFFSET.span.start ),
|
||||
end: DESER[u32]( POS_OFFSET.span.end ),
|
||||
start: start = DESER[u32]( POS_OFFSET.span.start ),
|
||||
end: end = DESER[u32]( POS_OFFSET.span.end ),
|
||||
...(RANGE && { range: [start, end] }),
|
||||
};
|
||||
}
|
||||
node
|
||||
|
||||
@@ -25,7 +25,7 @@ macro_rules! dummy_estree_impl {
|
||||
ts_type = "Dummy",
|
||||
raw_deser = "
|
||||
var nameSpan = DESER[NameSpan](POS);
|
||||
{kind: 'Name', name: nameSpan.value, start: nameSpan.start, end: nameSpan.end}
|
||||
{ kind: 'Name', name: nameSpan.value, start: nameSpan.start, end: nameSpan.end, ...(RANGE && { range: nameSpan.range }) }
|
||||
"
|
||||
)]
|
||||
pub struct ImportOrExportNameName<'a, 'b>(#[expect(dead_code)] pub &'b NameSpan<'a>);
|
||||
@@ -38,7 +38,7 @@ dummy_estree_impl!(ImportOrExportNameName<'_, '_>);
|
||||
ts_type = "Dummy",
|
||||
raw_deser = "
|
||||
var nameSpan = DESER[NameSpan](POS);
|
||||
{kind: 'Default', name: nameSpan.value, start: nameSpan.start, end: nameSpan.end}
|
||||
{ kind: 'Default', name: nameSpan.value, start: nameSpan.start, end: nameSpan.end, ...(RANGE && { range: nameSpan.range }) }
|
||||
"
|
||||
)]
|
||||
pub struct ExportLocalNameDefault<'a, 'b>(#[expect(dead_code)] pub &'b NameSpan<'a>);
|
||||
@@ -47,7 +47,10 @@ dummy_estree_impl!(ExportLocalNameDefault<'_, '_>);
|
||||
|
||||
/// Serializer for `Null` variant of `ExportLocalName`, `ExportExportName`, and `ExportImportName`.
|
||||
#[ast_meta]
|
||||
#[estree(ts_type = "Dummy", raw_deser = "{kind: 'None', name: null, start: null, end: null}")]
|
||||
#[estree(
|
||||
ts_type = "Dummy",
|
||||
raw_deser = "{ kind: 'None', name: null, start: null, end: null, ...(RANGE && { range: [null, null] }) }"
|
||||
)]
|
||||
pub struct ExportNameNull(pub ());
|
||||
|
||||
dummy_estree_impl!(ExportNameNull);
|
||||
@@ -57,8 +60,8 @@ dummy_estree_impl!(ExportNameNull);
|
||||
#[estree(
|
||||
ts_type = "Dummy",
|
||||
raw_deser = "
|
||||
var span = DESER[Span](POS);
|
||||
{kind: 'Default', name: null, start: span.start, end: span.end}
|
||||
var { start, end } = DESER[Span](POS);
|
||||
{ kind: 'Default', name: null, start, end, ...(RANGE && { range: [start, end] }) }
|
||||
"
|
||||
)]
|
||||
pub struct ImportOrExportNameDefault<'b>(#[expect(dead_code)] pub &'b Span);
|
||||
@@ -67,7 +70,10 @@ dummy_estree_impl!(ImportOrExportNameDefault<'_>);
|
||||
|
||||
/// Serializer for `All` variant of `ExportImportName`.
|
||||
#[ast_meta]
|
||||
#[estree(ts_type = "Dummy", raw_deser = "{kind: 'All', name: null, start: null, end: null}")]
|
||||
#[estree(
|
||||
ts_type = "Dummy",
|
||||
raw_deser = "{ kind: 'All', name: null, start: null, end: null, ...(RANGE && { range: [null, null] }) }"
|
||||
)]
|
||||
pub struct ExportImportNameAll(pub ());
|
||||
|
||||
dummy_estree_impl!(ExportImportNameAll);
|
||||
@@ -76,7 +82,7 @@ dummy_estree_impl!(ExportImportNameAll);
|
||||
#[ast_meta]
|
||||
#[estree(
|
||||
ts_type = "Dummy",
|
||||
raw_deser = "{kind: 'AllButDefault', name: null, start: null, end: null}"
|
||||
raw_deser = "{ kind: 'AllButDefault', name: null, start: null, end: null, ...(RANGE && { range: [null, null] }) }"
|
||||
)]
|
||||
pub struct ExportImportNameAllButDefault(pub ());
|
||||
|
||||
@@ -86,7 +92,7 @@ dummy_estree_impl!(ExportImportNameAllButDefault);
|
||||
#[ast_meta]
|
||||
#[estree(
|
||||
ts_type = "Dummy",
|
||||
raw_deser = "{kind: 'NamespaceObject', name: null, start: null, end: null}"
|
||||
raw_deser = "{ kind: 'NamespaceObject', name: null, start: null, end: null, ...(RANGE && { range: [null, null] }) }"
|
||||
)]
|
||||
pub struct ImportImportNameNamespaceObject(pub ());
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -42,6 +42,8 @@
|
||||
"generated/constants.js",
|
||||
"generated/deserialize/js.js",
|
||||
"generated/deserialize/ts.js",
|
||||
"generated/deserialize/js_range.js",
|
||||
"generated/deserialize/ts_range.js",
|
||||
"generated/lazy/constructors.js",
|
||||
"generated/lazy/types.js",
|
||||
"generated/lazy/walk.js",
|
||||
|
||||
@@ -35,7 +35,7 @@ if (!rawTransferSupported()) {
|
||||
export function parseSyncRawImpl(filename, sourceText, options, convert) {
|
||||
const { buffer, sourceByteLen } = prepareRaw(sourceText);
|
||||
parseSyncRawBinding(filename, buffer, sourceByteLen, options);
|
||||
return convert(buffer, sourceText, sourceByteLen);
|
||||
return convert(buffer, sourceText, sourceByteLen, !!options.range);
|
||||
}
|
||||
|
||||
// User should not schedule more async tasks than there are available CPUs, as it hurts performance,
|
||||
@@ -115,7 +115,7 @@ export async function parseAsyncRawImpl(filename, sourceText, options, convert)
|
||||
// Parse
|
||||
const { buffer, sourceByteLen } = prepareRaw(sourceText);
|
||||
await parseAsyncRawBinding(filename, buffer, sourceByteLen, options);
|
||||
const data = convert(buffer, sourceText, sourceByteLen);
|
||||
const data = convert(buffer, sourceText, sourceByteLen, !!options.range);
|
||||
|
||||
// Free the CPU core
|
||||
if (queue.length > 0) {
|
||||
|
||||
@@ -41,7 +41,11 @@ export function parseAsyncRaw(filename, sourceText, options) {
|
||||
return parseAsyncRawImpl(filename, sourceText, options, deserialize);
|
||||
}
|
||||
|
||||
let deserializeJS = null, deserializeTS = null;
|
||||
// Deserializers are large files, so lazy-loaded.
|
||||
// `deserialize` functions are stored in this array once loaded.
|
||||
// Index into these arrays is `isJs * 1 + range * 2`.
|
||||
const deserializers = [null, null, null, null];
|
||||
const deserializerNames = ['ts', 'js', 'ts_range', 'js_range'];
|
||||
|
||||
/**
|
||||
* Deserialize whole AST from buffer.
|
||||
@@ -49,32 +53,37 @@ let deserializeJS = null, deserializeTS = null;
|
||||
* @param {Uint8Array} buffer - Buffer containing AST in raw form
|
||||
* @param {string} sourceText - Source for the file
|
||||
* @param {number} sourceByteLen - Length of source text in UTF-8 bytes
|
||||
* @param {boolean} range - `true` if AST should contain `range` fields
|
||||
* @returns {Object} - Object with property getters for `program`, `module`, `comments`, and `errors`
|
||||
*/
|
||||
function deserialize(buffer, sourceText, sourceByteLen) {
|
||||
function deserialize(buffer, sourceText, sourceByteLen, range) {
|
||||
const isJs = isJsAst(buffer);
|
||||
|
||||
// Lazy load deserializer, and deserialize buffer to JS objects
|
||||
let data;
|
||||
if (isJsAst(buffer)) {
|
||||
if (deserializeJS === null) deserializeJS = require('../../generated/deserialize/js.js').deserialize;
|
||||
const deserializerIndex = (+isJs) | ((+range) << 1);
|
||||
let deserializeThis = deserializers[deserializerIndex];
|
||||
if (deserializeThis === null) {
|
||||
deserializeThis = deserializers[deserializerIndex] = require(
|
||||
`../../generated/deserialize/${deserializerNames[deserializerIndex]}.js`,
|
||||
).deserialize;
|
||||
}
|
||||
|
||||
// `preserveParens` argument is unconditionally `true` here. If `options` contains `preserveParens: false`,
|
||||
// `ParenthesizedExpression` nodes won't be in AST anyway, so the value is irrelevant.
|
||||
data = deserializeJS(buffer, sourceText, sourceByteLen, true);
|
||||
// `preserveParens` argument is unconditionally `true` here. If `options` contains `preserveParens: false`,
|
||||
// `ParenthesizedExpression` nodes won't be in AST anyway, so the value is irrelevant.
|
||||
const data = deserializeThis(buffer, sourceText, sourceByteLen, true);
|
||||
|
||||
// Add a line comment for hashbang
|
||||
// Add a line comment for hashbang if JS.
|
||||
// Do not add comment if TS, to match `@typescript-eslint/parser`.
|
||||
// See https://github.com/oxc-project/oxc/blob/ea784f5f082e4c53c98afde9bf983afd0b95e44e/napi/parser/src/lib.rs#L106-L130
|
||||
if (isJs) {
|
||||
const { hashbang } = data.program;
|
||||
if (hashbang !== null) {
|
||||
data.comments.unshift({ type: 'Line', value: hashbang.value, start: hashbang.start, end: hashbang.end });
|
||||
data.comments.unshift(
|
||||
range
|
||||
? { type: 'Line', value: hashbang.value, start: hashbang.start, end: hashbang.end, range: hashbang.range }
|
||||
: { type: 'Line', value: hashbang.value, start: hashbang.start, end: hashbang.end },
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (deserializeTS === null) deserializeTS = require('../../generated/deserialize/ts.js').deserialize;
|
||||
|
||||
// `preserveParens` argument is unconditionally `true` here. If `options` contains `preserveParens: false`,
|
||||
// `ParenthesizedExpression` nodes won't be in AST anyway, so the value is irrelevant.
|
||||
data = deserializeTS(buffer, sourceText, sourceByteLen, true);
|
||||
|
||||
// Note: Do not add line comment for hashbang, to match `@typescript-eslint/parser`.
|
||||
// See https://github.com/oxc-project/oxc/blob/ea784f5f082e4c53c98afde9bf983afd0b95e44e/napi/parser/src/lib.rs#L106-L130
|
||||
}
|
||||
|
||||
// Return buffer to cache, to be reused
|
||||
|
||||
@@ -314,3 +314,73 @@ describe.concurrent('`preserveParens` option', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.concurrent('`range` option', () => {
|
||||
describe.concurrent('should not include `range` fields when false', () => {
|
||||
it.concurrent('JS', async () => {
|
||||
const code = 'let x = 1;';
|
||||
|
||||
// @ts-ignore
|
||||
let ret = parseSync('test.js', code, { experimentalRawTransfer: true, range: false });
|
||||
expect(ret.errors.length).toBe(0);
|
||||
const { program } = ret;
|
||||
expect(program).not.toHaveProperty('range');
|
||||
const stmt = ret.program.body[0] as VariableDeclaration;
|
||||
expect(stmt).not.toHaveProperty('range');
|
||||
const declaration = stmt.declarations[0];
|
||||
expect(declaration).not.toHaveProperty('range');
|
||||
expect(declaration.id).not.toHaveProperty('range');
|
||||
expect(declaration.init).not.toHaveProperty('range');
|
||||
});
|
||||
|
||||
it.concurrent('TS', async () => {
|
||||
const code = 'let x = 1;';
|
||||
|
||||
// @ts-ignore
|
||||
let ret = parseSync('test.ts', code, { experimentalRawTransfer: true, range: false });
|
||||
expect(ret.errors.length).toBe(0);
|
||||
const { program } = ret;
|
||||
expect(program).not.toHaveProperty('range');
|
||||
const stmt = ret.program.body[0] as VariableDeclaration;
|
||||
expect(stmt).not.toHaveProperty('range');
|
||||
const declaration = stmt.declarations[0];
|
||||
expect(declaration).not.toHaveProperty('range');
|
||||
expect(declaration.id).not.toHaveProperty('range');
|
||||
expect(declaration.init).not.toHaveProperty('range');
|
||||
});
|
||||
});
|
||||
|
||||
describe.concurrent('should include `range` fields when true', () => {
|
||||
it.concurrent('JS', async () => {
|
||||
const code = 'let x = 1;';
|
||||
|
||||
// @ts-ignore
|
||||
let ret = parseSync('test.js', code, { experimentalRawTransfer: true, range: true });
|
||||
expect(ret.errors.length).toBe(0);
|
||||
const { program } = ret;
|
||||
expect(program.range).toEqual([0, 10]);
|
||||
const stmt = ret.program.body[0] as VariableDeclaration;
|
||||
expect(stmt.range).toEqual([0, 10]);
|
||||
const declaration = stmt.declarations[0];
|
||||
expect(declaration.range).toEqual([4, 9]);
|
||||
expect(declaration.id.range).toEqual([4, 5]);
|
||||
expect(declaration.init.range).toEqual([8, 9]);
|
||||
});
|
||||
|
||||
it.concurrent('TS', async () => {
|
||||
const code = 'let x = 1;';
|
||||
|
||||
// @ts-ignore
|
||||
let ret = parseSync('test.ts', code, { experimentalRawTransfer: true, range: true });
|
||||
expect(ret.errors.length).toBe(0);
|
||||
const { program } = ret;
|
||||
expect(program.range).toEqual([0, 10]);
|
||||
const stmt = ret.program.body[0] as VariableDeclaration;
|
||||
expect(stmt.range).toEqual([0, 10]);
|
||||
const declaration = stmt.declarations[0];
|
||||
expect(declaration.range).toEqual([4, 9]);
|
||||
expect(declaration.id.range).toEqual([4, 5]);
|
||||
expect(declaration.init.range).toEqual([8, 9]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -32,7 +32,7 @@ use crate::{
|
||||
output::Output,
|
||||
schema::{
|
||||
BoxDef, CellDef, Def, EnumDef, FieldDef, MetaType, OptionDef, PointerDef, PrimitiveDef,
|
||||
Schema, StructDef, TypeDef, VecDef,
|
||||
Schema, StructDef, TypeDef, TypeId, VecDef,
|
||||
extensions::layout::{GetLayout, GetOffset},
|
||||
},
|
||||
utils::{FxIndexMap, format_cow, number_lit, upper_case_first, write_it},
|
||||
@@ -74,7 +74,7 @@ impl Generator for RawTransferGenerator {
|
||||
fn generate_many(&self, schema: &Schema, codegen: &Codegen) -> Vec<Output> {
|
||||
let consts = get_constants(schema);
|
||||
|
||||
let Codes { js, ts } = generate_deserializers(consts, schema, codegen);
|
||||
let Codes { js, ts, js_range, ts_range } = generate_deserializers(consts, schema, codegen);
|
||||
let (constants_js, constants_rust) = generate_constants(consts);
|
||||
|
||||
vec![
|
||||
@@ -86,6 +86,14 @@ impl Generator for RawTransferGenerator {
|
||||
path: format!("{NAPI_PARSER_PACKAGE_PATH}/generated/deserialize/ts.js"),
|
||||
code: ts,
|
||||
},
|
||||
Output::Javascript {
|
||||
path: format!("{NAPI_PARSER_PACKAGE_PATH}/generated/deserialize/js_range.js"),
|
||||
code: js_range,
|
||||
},
|
||||
Output::Javascript {
|
||||
path: format!("{NAPI_PARSER_PACKAGE_PATH}/generated/deserialize/ts_range.js"),
|
||||
code: ts_range,
|
||||
},
|
||||
Output::Javascript {
|
||||
path: format!("{NAPI_PARSER_PACKAGE_PATH}/generated/constants.js"),
|
||||
code: constants_js.clone(),
|
||||
@@ -116,6 +124,10 @@ struct Codes {
|
||||
js: String,
|
||||
/// Code for TS deserializer
|
||||
ts: String,
|
||||
/// Code for JS deserializer with `range` fields
|
||||
js_range: String,
|
||||
/// Code for TS deserializer with `range` fields
|
||||
ts_range: String,
|
||||
}
|
||||
|
||||
/// Generate deserializer functions for all types.
|
||||
@@ -130,6 +142,7 @@ struct Codes {
|
||||
/// and minifier then shakes out the dead code for each.
|
||||
fn generate_deserializers(consts: Constants, schema: &Schema, codegen: &Codegen) -> Codes {
|
||||
let estree_derive_id = codegen.get_derive_id_by_name("ESTree");
|
||||
let span_type_id = schema.type_names["Span"];
|
||||
|
||||
// Prelude to generated deserializer.
|
||||
// Defines the main `deserialize` function.
|
||||
@@ -140,6 +153,7 @@ fn generate_deserializers(consts: Constants, schema: &Schema, codegen: &Codegen)
|
||||
let uint8, uint32, float64, sourceText, sourceIsAscii, sourceByteLen, preserveParens;
|
||||
|
||||
const IS_TS = false;
|
||||
const RANGE = false;
|
||||
|
||||
const textDecoder = new TextDecoder('utf-8', {{ ignoreBOM: true }}),
|
||||
decodeStr = textDecoder.decode.bind(textDecoder),
|
||||
@@ -174,7 +188,7 @@ fn generate_deserializers(consts: Constants, schema: &Schema, codegen: &Codegen)
|
||||
for type_def in &schema.types {
|
||||
match type_def {
|
||||
TypeDef::Struct(struct_def) => {
|
||||
generate_struct(struct_def, &mut code, estree_derive_id, schema);
|
||||
generate_struct(struct_def, &mut code, span_type_id, estree_derive_id, schema);
|
||||
}
|
||||
TypeDef::Enum(enum_def) => {
|
||||
generate_enum(enum_def, &mut code, estree_derive_id, schema);
|
||||
@@ -212,10 +226,18 @@ fn generate_deserializers(consts: Constants, schema: &Schema, codegen: &Codegen)
|
||||
let mut program_ts = program_js.clone_in(&allocator);
|
||||
replace_const(&mut program_ts, "IS_TS", true);
|
||||
|
||||
// Create clones of AST for with-`range` field deserializer with `const RANGE = true;`
|
||||
let mut program_js_range = program_js.clone_in(&allocator);
|
||||
replace_const(&mut program_js_range, "RANGE", true);
|
||||
let mut program_ts_range = program_ts.clone_in(&allocator);
|
||||
replace_const(&mut program_ts_range, "RANGE", true);
|
||||
|
||||
// Print both deserializers, using minifier to shake out JS/TS-specific code from each
|
||||
Codes {
|
||||
js: print_minified(&mut program_js, &allocator),
|
||||
ts: print_minified(&mut program_ts, &allocator),
|
||||
js_range: print_minified(&mut program_js_range, &allocator),
|
||||
ts_range: print_minified(&mut program_ts_range, &allocator),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,6 +253,7 @@ enum DeserializerType {
|
||||
fn generate_struct(
|
||||
struct_def: &StructDef,
|
||||
code: &mut String,
|
||||
span_type_id: TypeId,
|
||||
estree_derive_id: DeriveId,
|
||||
schema: &Schema,
|
||||
) {
|
||||
@@ -239,7 +262,7 @@ fn generate_struct(
|
||||
}
|
||||
|
||||
let fn_name = struct_def.deser_name(schema);
|
||||
let mut generator = StructDeserializerGenerator::new(schema);
|
||||
let mut generator = StructDeserializerGenerator::new(span_type_id, schema);
|
||||
|
||||
let body = struct_def.estree.via.as_deref().and_then(|converter_name| {
|
||||
let converter = schema.meta_by_name(converter_name);
|
||||
@@ -330,6 +353,8 @@ struct StructDeserializerGenerator<'s> {
|
||||
preamble: Vec<String>,
|
||||
/// Fields, keyed by fields name (field name in ESTree AST)
|
||||
fields: FxIndexMap<String, StructFieldValue>,
|
||||
/// `TypeId` for `Span`
|
||||
span_type_id: TypeId,
|
||||
/// Schema
|
||||
schema: &'s Schema,
|
||||
}
|
||||
@@ -342,11 +367,12 @@ struct StructFieldValue {
|
||||
}
|
||||
|
||||
impl<'s> StructDeserializerGenerator<'s> {
|
||||
fn new(schema: &'s Schema) -> Self {
|
||||
fn new(span_type_id: TypeId, schema: &'s Schema) -> Self {
|
||||
Self {
|
||||
dependent_field_names: FxHashSet::default(),
|
||||
preamble: vec![],
|
||||
fields: FxIndexMap::default(),
|
||||
span_type_id,
|
||||
schema,
|
||||
}
|
||||
}
|
||||
@@ -407,7 +433,8 @@ impl<'s> StructDeserializerGenerator<'s> {
|
||||
}
|
||||
|
||||
let field_name = get_struct_field_name(field).to_string();
|
||||
let field_type = field.type_def(self.schema);
|
||||
let field_type_id = field.type_id;
|
||||
let field_type = &self.schema.types[field_type_id];
|
||||
let field_offset = struct_offset + field.offset_64();
|
||||
|
||||
if should_flatten_field(field, self.schema) {
|
||||
@@ -430,6 +457,19 @@ impl<'s> StructDeserializerGenerator<'s> {
|
||||
field.name(),
|
||||
),
|
||||
}
|
||||
|
||||
if field_type_id == self.span_type_id {
|
||||
self.fields.insert(
|
||||
"range".to_string(),
|
||||
StructFieldValue {
|
||||
value: "...(RANGE && { range: [start, end] })".to_string(),
|
||||
deser_type,
|
||||
},
|
||||
);
|
||||
|
||||
self.dependent_field_names.extend(["start".to_string(), "end".to_string()]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user