Respect original text decorator order (#5207)
parent
023a8b78df
commit
62e4654e60
|
@ -2,16 +2,18 @@
|
||||||
|
|
||||||
const getLast = require("../utils/get-last");
|
const getLast = require("../utils/get-last");
|
||||||
|
|
||||||
function locStart(node) {
|
function locStart(node, opts) {
|
||||||
|
opts = opts || {};
|
||||||
// Handle nodes with decorators. They should start at the first decorator
|
// Handle nodes with decorators. They should start at the first decorator
|
||||||
if (
|
if (
|
||||||
|
!opts.ignoreDecorators &&
|
||||||
node.declaration &&
|
node.declaration &&
|
||||||
node.declaration.decorators &&
|
node.declaration.decorators &&
|
||||||
node.declaration.decorators.length > 0
|
node.declaration.decorators.length > 0
|
||||||
) {
|
) {
|
||||||
return locStart(node.declaration.decorators[0]);
|
return locStart(node.declaration.decorators[0]);
|
||||||
}
|
}
|
||||||
if (node.decorators && node.decorators.length > 0) {
|
if (!opts.ignoreDecorators && node.decorators && node.decorators.length > 0) {
|
||||||
return locStart(node.decorators[0]);
|
return locStart(node.decorators[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,71 +4,93 @@ const createError = require("../common/parser-create-error");
|
||||||
const hasPragma = require("./pragma").hasPragma;
|
const hasPragma = require("./pragma").hasPragma;
|
||||||
const locFns = require("./loc");
|
const locFns = require("./loc");
|
||||||
|
|
||||||
|
function babylonOptions(extraOptions, extraPlugins) {
|
||||||
|
return Object.assign(
|
||||||
|
{
|
||||||
|
sourceType: "module",
|
||||||
|
allowAwaitOutsideFunction: true,
|
||||||
|
allowImportExportEverywhere: true,
|
||||||
|
allowReturnOutsideFunction: true,
|
||||||
|
allowSuperOutsideMethod: true,
|
||||||
|
plugins: [
|
||||||
|
"jsx",
|
||||||
|
"flow",
|
||||||
|
"doExpressions",
|
||||||
|
"objectRestSpread",
|
||||||
|
"classProperties",
|
||||||
|
"exportDefaultFrom",
|
||||||
|
"exportNamespaceFrom",
|
||||||
|
"asyncGenerators",
|
||||||
|
"functionBind",
|
||||||
|
"functionSent",
|
||||||
|
"dynamicImport",
|
||||||
|
"numericSeparator",
|
||||||
|
"importMeta",
|
||||||
|
"optionalCatchBinding",
|
||||||
|
"optionalChaining",
|
||||||
|
"classPrivateProperties",
|
||||||
|
["pipelineOperator", { proposal: "minimal" }],
|
||||||
|
"nullishCoalescingOperator",
|
||||||
|
"bigInt",
|
||||||
|
"throwExpressions"
|
||||||
|
].concat(extraPlugins)
|
||||||
|
},
|
||||||
|
extraOptions
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function parse(text, parsers, opts) {
|
function parse(text, parsers, opts) {
|
||||||
// Inline the require to avoid loading all the JS if we don't use it
|
// Inline the require to avoid loading all the JS if we don't use it
|
||||||
const babylon = require("@babel/parser");
|
const babylon = require("@babel/parser");
|
||||||
|
|
||||||
const babylonOptions = {
|
const combinations = [
|
||||||
sourceType: "module",
|
babylonOptions({ strictMode: true }, ["decorators-legacy"]),
|
||||||
allowAwaitOutsideFunction: true,
|
babylonOptions({ strictMode: false }, ["decorators-legacy"]),
|
||||||
allowImportExportEverywhere: true,
|
babylonOptions({ strictMode: true }, [
|
||||||
allowReturnOutsideFunction: true,
|
["decorators", { decoratorsBeforeExport: false }]
|
||||||
allowSuperOutsideMethod: true,
|
]),
|
||||||
plugins: [
|
babylonOptions({ strictMode: false }, [
|
||||||
"jsx",
|
["decorators", { decoratorsBeforeExport: false }]
|
||||||
"flow",
|
])
|
||||||
"doExpressions",
|
];
|
||||||
"objectRestSpread",
|
|
||||||
"decorators-legacy",
|
|
||||||
"classProperties",
|
|
||||||
"exportDefaultFrom",
|
|
||||||
"exportNamespaceFrom",
|
|
||||||
"asyncGenerators",
|
|
||||||
"functionBind",
|
|
||||||
"functionSent",
|
|
||||||
"dynamicImport",
|
|
||||||
"numericSeparator",
|
|
||||||
"importMeta",
|
|
||||||
"optionalCatchBinding",
|
|
||||||
"optionalChaining",
|
|
||||||
"classPrivateProperties",
|
|
||||||
["pipelineOperator", { proposal: "minimal" }],
|
|
||||||
"nullishCoalescingOperator",
|
|
||||||
"bigInt",
|
|
||||||
"throwExpressions"
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
const parseMethod =
|
const parseMethod =
|
||||||
!opts || opts.parser === "babylon" ? "parse" : "parseExpression";
|
!opts || opts.parser === "babylon" ? "parse" : "parseExpression";
|
||||||
|
|
||||||
let ast;
|
let ast;
|
||||||
try {
|
try {
|
||||||
ast = babylon[parseMethod](text, babylonOptions);
|
ast = tryCombinations(babylon[parseMethod].bind(null, text), combinations);
|
||||||
} catch (originalError) {
|
} catch (error) {
|
||||||
try {
|
throw createError(
|
||||||
ast = babylon[parseMethod](
|
// babel error prints (l:c) with cols that are zero indexed
|
||||||
text,
|
// so we need our custom error
|
||||||
Object.assign({}, babylonOptions, { strictMode: false })
|
error.message.replace(/ \(.*\)/, ""),
|
||||||
);
|
{
|
||||||
} catch (nonStrictError) {
|
start: {
|
||||||
throw createError(
|
line: error.loc.line,
|
||||||
// babel error prints (l:c) with cols that are zero indexed
|
column: error.loc.column + 1
|
||||||
// so we need our custom error
|
|
||||||
originalError.message.replace(/ \(.*\)/, ""),
|
|
||||||
{
|
|
||||||
start: {
|
|
||||||
line: originalError.loc.line,
|
|
||||||
column: originalError.loc.column + 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
delete ast.tokens;
|
delete ast.tokens;
|
||||||
return ast;
|
return ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tryCombinations(fn, combinations) {
|
||||||
|
let error;
|
||||||
|
for (let i = 0; i < combinations.length; i++) {
|
||||||
|
try {
|
||||||
|
return fn(combinations[i]);
|
||||||
|
} catch (_error) {
|
||||||
|
if (!error) {
|
||||||
|
error = _error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
function parseJson(text, parsers, opts) {
|
function parseJson(text, parsers, opts) {
|
||||||
const ast = parse(text, parsers, Object.assign({}, opts, { parser: "json" }));
|
const ast = parse(text, parsers, Object.assign({}, opts, { parser: "json" }));
|
||||||
|
|
||||||
|
|
|
@ -89,13 +89,19 @@ function genericPrint(path, options, printPath, args) {
|
||||||
return linesWithoutParens;
|
return linesWithoutParens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const parentExportDecl = getParentExportDeclaration(path);
|
||||||
const decorators = [];
|
const decorators = [];
|
||||||
if (
|
if (
|
||||||
node.decorators &&
|
node.decorators &&
|
||||||
node.decorators.length > 0 &&
|
node.decorators.length > 0 &&
|
||||||
// If the parent node is an export declaration, it will be
|
// If the parent node is an export declaration and the decorator
|
||||||
// responsible for printing node.decorators.
|
// was written before the export, the export will be responsible
|
||||||
!getParentExportDeclaration(path)
|
// for printing the decorators.
|
||||||
|
!(
|
||||||
|
parentExportDecl &&
|
||||||
|
options.locStart(parentExportDecl, { ignoreDecorators: true }) >
|
||||||
|
options.locStart(node.decorators[0])
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
const shouldBreak =
|
const shouldBreak =
|
||||||
node.type === "ClassDeclaration" ||
|
node.type === "ClassDeclaration" ||
|
||||||
|
@ -121,10 +127,19 @@ function genericPrint(path, options, printPath, args) {
|
||||||
|
|
||||||
decorators.push(printPath(decoratorPath), separator);
|
decorators.push(printPath(decoratorPath), separator);
|
||||||
}, "decorators");
|
}, "decorators");
|
||||||
|
|
||||||
|
if (parentExportDecl) {
|
||||||
|
decorators.unshift(hardline);
|
||||||
|
}
|
||||||
} else if (
|
} else if (
|
||||||
isExportDeclaration(node) &&
|
isExportDeclaration(node) &&
|
||||||
node.declaration &&
|
node.declaration &&
|
||||||
node.declaration.decorators
|
node.declaration.decorators &&
|
||||||
|
node.declaration.decorators.length > 0 &&
|
||||||
|
// Only print decorators here if they were written before the export,
|
||||||
|
// otherwise they are printed by the node.declaration
|
||||||
|
options.locStart(node, { ignoreDecorators: true }) >
|
||||||
|
options.locStart(node.declaration.decorators[0])
|
||||||
) {
|
) {
|
||||||
// Export declarations are responsible for printing any decorators
|
// Export declarations are responsible for printing any decorators
|
||||||
// that logically apply to node.declaration.
|
// that logically apply to node.declaration.
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`after_export.js - babylon-verify 1`] = `
|
||||||
|
export @decorator class Foo {}
|
||||||
|
|
||||||
|
export default @decorator class {}
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
export
|
||||||
|
@decorator
|
||||||
|
class Foo {}
|
||||||
|
|
||||||
|
export default
|
||||||
|
@decorator
|
||||||
|
class {}
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`before_export.js - babylon-verify 1`] = `
|
||||||
|
@decorator
|
||||||
|
export class Foo {}
|
||||||
|
|
||||||
|
@decorator
|
||||||
|
export default class {}
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
@decorator
|
||||||
|
export class Foo {}
|
||||||
|
|
||||||
|
@decorator
|
||||||
|
export default class {}
|
||||||
|
|
||||||
|
`;
|
|
@ -0,0 +1,3 @@
|
||||||
|
export @decorator class Foo {}
|
||||||
|
|
||||||
|
export default @decorator class {}
|
|
@ -0,0 +1,5 @@
|
||||||
|
@decorator
|
||||||
|
export class Foo {}
|
||||||
|
|
||||||
|
@decorator
|
||||||
|
export default class {}
|
|
@ -0,0 +1,2 @@
|
||||||
|
// TypeScript and Flow don't accept decorator after export
|
||||||
|
run_spec(__dirname, ["babylon"]);
|
Loading…
Reference in New Issue