Skip to content

Commit 2ce56e8

Browse files
JLHwungPranav2612000nicolo-ribaudo
authored
[babel 8] Disallow synchronous usage of babel.* callback methods (#12695)
* [babel 8] Disallow synchronous usage of `babel.{parse,transform{,FromAst,File}}` Disallow babel.transformFromAst synchronously Disallow using babel.transform, babel.transformFile, babel.parse synchronously chore: throw error behind BABEL_8_BREAKING flag fix: use transformFromAstSync fix: use transformSync refactor: use Babel.transformSync in standalone fix: use parseSync feat: emit deprecation message on callback-less usage chore: add tests on thrown error call sync method on undefined callback review comments address review comments chore: use loadOptionsSync for OptionManager Co-authored-by: Huáng Jùnliàng <[email protected]> Co-authored-by: Nicolò Ribaudo <[email protected]> * Comment out warning for now Co-authored-by: Pranav <[email protected]> Co-authored-by: Nicolò Ribaudo <[email protected]>
1 parent ad19401 commit 2ce56e8

19 files changed

Lines changed: 225 additions & 138 deletions

File tree

packages/babel-core/src/config/full.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ const validateIfOptionNeedsFilename = (
430430
[
431431
`Preset ${formattedPresetName} requires a filename to be set when babel is called directly,`,
432432
`\`\`\``,
433-
`babel.transform(code, { filename: 'file.ts', presets: [${formattedPresetName}] });`,
433+
`babel.transformSync(code, { filename: 'file.ts', presets: [${formattedPresetName}] });`,
434434
`\`\`\``,
435435
`See https://babeljs.io/docs/en/options#filename for more information.`,
436436
].join("\n"),

packages/babel-core/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ export const DEFAULT_EXTENSIONS = Object.freeze([
6363
] as const);
6464

6565
// For easier backward-compatibility, provide an API like the one we exposed in Babel 6.
66-
import { loadOptions } from "./config";
66+
import { loadOptionsSync } from "./config";
6767
export class OptionManager {
6868
init(opts: {}) {
69-
return loadOptions(opts);
69+
return loadOptionsSync(opts);
7070
}
7171
}
7272

packages/babel-core/src/parse.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,18 @@ export const parse: Parse = function parse(code, opts?, callback?) {
3939
opts = undefined;
4040
}
4141

42-
// For backward-compat with Babel 7's early betas, we allow sync parsing when
43-
// no callback is given. Will be dropped in some future Babel major version.
44-
if (callback === undefined) return parseRunner.sync(code, opts);
42+
if (callback === undefined) {
43+
if (process.env.BABEL_8_BREAKING) {
44+
throw new Error(
45+
"Starting from Babel 8.0.0, the 'parse' function expects a callback. If you need to call it synchronously, please use 'parseSync'.",
46+
);
47+
} else {
48+
// console.warn(
49+
// "Starting from Babel 8.0.0, the 'parse' function will expect a callback. If you need to call it synchronously, please use 'parseSync'.",
50+
// );
51+
return parseRunner.sync(code, opts);
52+
}
53+
}
4554

4655
parseRunner.errback(code, opts, callback);
4756
};

packages/babel-core/src/transform-ast.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,17 @@ export const transformFromAst: TransformFromAst = function transformFromAst(
4545
opts = undefined;
4646
}
4747

48-
// For backward-compat with Babel 6, we allow sync transformation when
49-
// no callback is given. Will be dropped in some future Babel major version.
5048
if (callback === undefined) {
51-
return transformFromAstRunner.sync(ast, code, opts);
49+
if (process.env.BABEL_8_BREAKING) {
50+
throw new Error(
51+
"Starting from Babel 8.0.0, the 'transformFromAst' function expects a callback. If you need to call it synchronously, please use 'transformFromAstSync'.",
52+
);
53+
} else {
54+
// console.warn(
55+
// "Starting from Babel 8.0.0, the 'transformFromAst' function will expect a callback. If you need to call it synchronously, please use 'transformFromAstSync'.",
56+
// );
57+
return transformFromAstRunner.sync(ast, code, opts);
58+
}
5259
}
5360

5461
transformFromAstRunner.errback(ast, code, opts, callback);

packages/babel-core/src/transform.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,18 @@ export const transform: Transform = function transform(code, opts?, callback?) {
3131
opts = undefined;
3232
}
3333

34-
// For backward-compat with Babel 6, we allow sync transformation when
35-
// no callback is given. Will be dropped in some future Babel major version.
36-
if (callback === undefined) return transformRunner.sync(code, opts);
34+
if (callback === undefined) {
35+
if (process.env.BABEL_8_BREAKING) {
36+
throw new Error(
37+
"Starting from Babel 8.0.0, the 'transform' function expects a callback. If you need to call it synchronously, please use 'transformSync'.",
38+
);
39+
} else {
40+
// console.warn(
41+
// "Starting from Babel 8.0.0, the 'transform' function will expect a callback. If you need to call it synchronously, please use 'transformSync'.",
42+
// );
43+
return transformRunner.sync(code, opts);
44+
}
45+
}
3746

3847
transformRunner.errback(code, opts, callback);
3948
};

packages/babel-core/test/api.js

Lines changed: 82 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,18 @@ function parse(code, opts) {
2525
return babel.parse(code, { cwd, configFile: false, ...opts });
2626
}
2727

28+
function parseSync(code, opts) {
29+
return babel.parseSync(code, { cwd, configFile: false, ...opts });
30+
}
31+
2832
function transform(code, opts) {
2933
return babel.transform(code, { cwd, configFile: false, ...opts });
3034
}
3135

36+
function transformSync(code, opts) {
37+
return babel.transformSync(code, { cwd, configFile: false, ...opts });
38+
}
39+
3240
function transformFile(filename, opts, cb) {
3341
return babel.transformFile(filename, { cwd, configFile: false, ...opts }, cb);
3442
}
@@ -51,6 +59,14 @@ function transformFromAst(ast, code, opts) {
5159
return babel.transformFromAst(ast, code, { cwd, configFile: false, ...opts });
5260
}
5361

62+
function transformFromAstSync(ast, code, opts) {
63+
return babel.transformFromAstSync(ast, code, {
64+
cwd,
65+
configFile: false,
66+
...opts,
67+
});
68+
}
69+
5470
describe("parser and generator options", function () {
5571
const recast = {
5672
parse: function (code, opts) {
@@ -62,7 +78,7 @@ describe("parser and generator options", function () {
6278
};
6379

6480
function newTransform(string) {
65-
return transform(string, {
81+
return transformSync(string, {
6682
ast: true,
6783
parserOpts: {
6884
parser: recast.parse,
@@ -78,7 +94,7 @@ describe("parser and generator options", function () {
7894
it("options", function () {
7995
const string = "original;";
8096
expect(newTransform(string).ast).toEqual(
81-
transform(string, { ast: true }).ast,
97+
transformSync(string, { ast: true }).ast,
8298
);
8399
expect(newTransform(string).code).toBe(string);
84100
});
@@ -87,7 +103,7 @@ describe("parser and generator options", function () {
87103
const experimental = "var a: number = 1;";
88104

89105
expect(newTransform(experimental).ast).toEqual(
90-
transform(experimental, {
106+
transformSync(experimental, {
91107
ast: true,
92108
parserOpts: {
93109
plugins: ["flow"],
@@ -97,7 +113,7 @@ describe("parser and generator options", function () {
97113
expect(newTransform(experimental).code).toBe(experimental);
98114

99115
function newTransformWithPlugins(string) {
100-
return transform(string, {
116+
return transformSync(string, {
101117
ast: true,
102118
plugins: [cwd + "/../../babel-plugin-syntax-flow"],
103119
parserOpts: {
@@ -110,7 +126,7 @@ describe("parser and generator options", function () {
110126
}
111127

112128
expect(newTransformWithPlugins(experimental).ast).toEqual(
113-
transform(experimental, {
129+
transformSync(experimental, {
114130
ast: true,
115131
parserOpts: {
116132
plugins: ["flow"],
@@ -124,7 +140,7 @@ describe("parser and generator options", function () {
124140
const experimental = "if (true) {\n import a from 'a';\n}";
125141

126142
expect(newTransform(experimental).ast).not.toBe(
127-
transform(experimental, {
143+
transformSync(experimental, {
128144
ast: true,
129145
parserOpts: {
130146
allowImportExportEverywhere: true,
@@ -156,6 +172,27 @@ describe("api", function () {
156172
expect(babel.tokTypes).toBeDefined();
157173
});
158174

175+
(process.env.BABEL_8_BREAKING ? it : it.skip)(
176+
"parse throws on undefined callback",
177+
() => {
178+
expect(() => parse("", {})).toThrowErrorMatchingInlineSnapshot(
179+
`"Starting from Babel 8.0.0, the 'parse' function expects a callback. If you need to call it synchronously, please use 'parseSync'."`,
180+
);
181+
},
182+
);
183+
184+
(process.env.BABEL_8_BREAKING ? it : it.skip)(
185+
"transform throws on undefined callback",
186+
() => {
187+
const options = {
188+
filename: "example.js",
189+
};
190+
expect(() => transform("", options)).toThrowErrorMatchingInlineSnapshot(
191+
`"Starting from Babel 8.0.0, the 'transform' function expects a callback. If you need to call it synchronously, please use 'transformSync'."`,
192+
);
193+
},
194+
);
195+
159196
it("transformFile", function () {
160197
const options = {
161198
babelrc: false,
@@ -189,6 +226,16 @@ describe("api", function () {
189226
// keep user options untouched
190227
expect(options).toEqual({ babelrc: false });
191228
});
229+
it("transformFile throws on undefined callback", () => {
230+
const options = {
231+
babelrc: false,
232+
};
233+
expect(() =>
234+
transformFile(cwd + "/fixtures/api/file.js", options),
235+
).toThrowErrorMatchingInlineSnapshot(
236+
`"Asynchronous function called without callback"`,
237+
);
238+
});
192239

193240
it("transformFileSync", function () {
194241
const options = {
@@ -201,10 +248,23 @@ describe("api", function () {
201248
expect(options).toEqual({ babelrc: false });
202249
});
203250

204-
it("transformFromAst should not mutate the AST", function () {
251+
(process.env.BABEL_8_BREAKING ? it : it.skip)(
252+
"transformFromAst throws on undefined callback",
253+
() => {
254+
const program = "const identifier = 1";
255+
const node = parseSync(program);
256+
expect(() =>
257+
transformFromAst(node, program),
258+
).toThrowErrorMatchingInlineSnapshot(
259+
`"Starting from Babel 8.0.0, the 'transformFromAst' function expects a callback. If you need to call it synchronously, please use 'transformFromAstSync'."`,
260+
);
261+
},
262+
);
263+
264+
it("transformFromAstSync should not mutate the AST", function () {
205265
const program = "const identifier = 1";
206-
const node = parse(program);
207-
const { code } = transformFromAst(node, program, {
266+
const node = parseSync(program);
267+
const { code } = transformFromAstSync(node, program, {
208268
plugins: [
209269
function () {
210270
return {
@@ -225,10 +285,10 @@ describe("api", function () {
225285
);
226286
});
227287

228-
it("transformFromAst should mutate the AST when cloneInputAst is false", function () {
288+
it("transformFromAstSync should mutate the AST when cloneInputAst is false", function () {
229289
const program = "const identifier = 1";
230-
const node = parse(program);
231-
const { code } = transformFromAst(node, program, {
290+
const node = parseSync(program);
291+
const { code } = transformFromAstSync(node, program, {
232292
cloneInputAst: false,
233293
plugins: [
234294
function () {
@@ -252,7 +312,7 @@ describe("api", function () {
252312

253313
it("options throw on falsy true", function () {
254314
return expect(function () {
255-
transform("", {
315+
transformSync("", {
256316
plugins: [cwd + "/../../babel-plugin-syntax-jsx", false],
257317
});
258318
}).toThrow(/.plugins\[1\] must be a string, object, function/);
@@ -273,7 +333,7 @@ describe("api", function () {
273333
let calledRaw = 0;
274334
let calledIntercept = 0;
275335

276-
transform("function foo() { bar(foobar); }", {
336+
transformSync("function foo() { bar(foobar); }", {
277337
wrapPluginVisitorMethod: function (pluginAlias, visitorType, callback) {
278338
if (pluginAlias !== "foobar") {
279339
return callback;
@@ -307,7 +367,7 @@ describe("api", function () {
307367
let aliasBaseType = null;
308368

309369
function execTest(passPerPreset) {
310-
return transform("type Foo = number; let x = (y): Foo => y;", {
370+
return transformSync("type Foo = number; let x = (y): Foo => y;", {
311371
sourceType: "script",
312372
passPerPreset: passPerPreset,
313373
presets: [
@@ -390,7 +450,7 @@ describe("api", function () {
390450
const oldEnv = process.env.BABEL_ENV;
391451
process.env.BABEL_ENV = "development";
392452

393-
const result = transform("", {
453+
const result = transformSync("", {
394454
cwd: path.join(cwd, "fixtures", "config", "complex-plugin-config"),
395455
filename: path.join(
396456
cwd,
@@ -448,7 +508,7 @@ describe("api", function () {
448508

449509
it("interpreter directive backward-compat", function () {
450510
function doTransform(code, preHandler) {
451-
return transform(code, {
511+
return transformSync(code, {
452512
plugins: [
453513
{
454514
pre: preHandler,
@@ -501,7 +561,7 @@ describe("api", function () {
501561
});
502562

503563
it("source map merging", function () {
504-
const result = transform(
564+
const result = transformSync(
505565
[
506566
/* eslint-disable max-len */
507567
'function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }',
@@ -710,7 +770,7 @@ describe("api", function () {
710770
});
711771

712772
it("default", function () {
713-
const result = transform("foo;", {
773+
const result = transformSync("foo;", {
714774
env: {
715775
development: { comments: false },
716776
},
@@ -721,7 +781,7 @@ describe("api", function () {
721781

722782
it("BABEL_ENV", function () {
723783
process.env.BABEL_ENV = "foo";
724-
const result = transform("foo;", {
784+
const result = transformSync("foo;", {
725785
env: {
726786
foo: { comments: false },
727787
},
@@ -731,7 +791,7 @@ describe("api", function () {
731791

732792
it("NODE_ENV", function () {
733793
process.env.NODE_ENV = "foo";
734-
const result = transform("foo;", {
794+
const result = transformSync("foo;", {
735795
env: {
736796
foo: { comments: false },
737797
},
@@ -847,7 +907,7 @@ describe("api", function () {
847907
},
848908
],
849909
}),
850-
).toThrow();
910+
).toThrowErrorMatchingInlineSnapshot(`"unknown: Unknown helper fooBar"`);
851911
});
852912
});
853913
});

packages/babel-core/test/parse.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import fs from "fs";
22
import path from "path";
3-
import { parse } from "../lib/index.js";
3+
import { parseSync } from "../lib/index.js";
44
import { fileURLToPath } from "url";
55
import { createRequire } from "module";
66

@@ -15,12 +15,12 @@ function fixture(...args) {
1515
);
1616
}
1717

18-
describe("parse", function () {
18+
describe("parseSync", function () {
1919
it("should parse using configuration from .babelrc when a filename is provided", function () {
2020
const input = fs.readFileSync(fixture("input.js"), "utf8");
2121
const output = require(fixture("output"));
2222

23-
const result = parse(input, {
23+
const result = parseSync(input, {
2424
filename: fixture("input.js"),
2525
cwd: fixture(),
2626
});
@@ -31,7 +31,7 @@ describe("parse", function () {
3131
const input = fs.readFileSync(fixture("input.js"), "utf8");
3232
const output = require(fixture("output.json"));
3333

34-
const result = parse(input, {
34+
const result = parseSync(input, {
3535
parserOpts: {
3636
plugins: [["decorators", { decoratorsBeforeExport: false }]],
3737
},

0 commit comments

Comments
 (0)