Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions packages/babel-cli/src/babel/dir.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { sync as makeDirSync } from "make-dir";
import slash from "slash";
import path from "path";
import fs from "fs";
import { DEFAULT_EXTENSIONS } from "@babel/core";

import * as util from "./util";
import { type CmdOptions } from "./options";
Expand Down Expand Up @@ -34,7 +35,10 @@ export default async function ({
): Promise<$Keys<typeof FILE_TYPE>> {
let relative = path.relative(base, src);

if (!util.isCompilableExtension(relative, cliOptions.extensions)) {
if (
!util.BABEL_SUPPORTS_EXTENSIONS_OPTION &&
!util.isCompilableExtension(relative, cliOptions.extensions)
) {
return FILE_TYPE.NON_COMPILABLE;
}

Expand All @@ -48,17 +52,28 @@ export default async function ({
const dest = getDest(relative, base);

try {
const res = await util.compile(
src,
const config = await util.loadPartialConfig(
defaults(
{
sourceFileName: slash(path.relative(dest + "/..", src)),
filename: src,
...(util.BABEL_SUPPORTS_EXTENSIONS_OPTION && {
// TODO(Babel 8): At some point @babel/core will default to DEFAULT_EXTENSIONS
// instead of ["*"], and we can avoid setting it here.
extensions: cliOptions.extensions ?? DEFAULT_EXTENSIONS,
}),
},
babelOptions,
),
);

if (!res) return FILE_TYPE.IGNORED;
if (!config) return FILE_TYPE.IGNORED;

const res = await util.compile(src, config.options);

// If loadPartialConfig didn't return null, it's because the file wasn't ignored.
// Thus, if compiling the file returns null, it's because the extension isn't supported.
if (!res) return FILE_TYPE.NON_COMPILABLE;

// we've requested explicit sourcemaps to be written to disk
if (
Expand Down
1 change: 1 addition & 0 deletions packages/babel-cli/src/babel/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ export default async function ({
})
.on("all", function (type: string, filename: string): void {
if (
!util.BABEL_SUPPORTS_EXTENSIONS_OPTION &&
!util.isCompilableExtension(filename, cliOptions.extensions) &&
!filenames.includes(filename)
) {
Expand Down
29 changes: 29 additions & 0 deletions packages/babel-cli/src/babel/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import * as babel from "@babel/core";
import path from "path";
import fs from "fs";

// Technically we could use the "semver" package here, but (for exmaple)
// parseFloat("4.23.6") returns 4.23 so it's "good enough"
export const BABEL_SUPPORTS_EXTENSIONS_OPTION =
parseFloat(babel.version) >= 7.11;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️

  • Before merging this PR, I need to add a warning in new-version in Makefile to replace 7.11 with the actual new version.


export function chmod(src: string, dest: string): void {
fs.chmodSync(dest, fs.statSync(src).mode);
}
Expand Down Expand Up @@ -32,6 +37,10 @@ export function readdirForCompilable(
includeDotfiles: boolean,
altExts?: Array<string>,
): Array<string> {
if (BABEL_SUPPORTS_EXTENSIONS_OPTION) {
return readdir(dirname, includeDotfiles);
}

return readdir(dirname, includeDotfiles, function (filename) {
return isCompilableExtension(filename, altExts);
});
Expand All @@ -44,6 +53,12 @@ export function isCompilableExtension(
filename: string,
altExts?: Array<string>,
): boolean {
if (!BABEL_SUPPORTS_EXTENSIONS_OPTION) {
throw new Error(
"Internal @babel/cli error: isCompilableExtension is only supported with old @babel/core versions.",
);
}

const exts = altExts || babel.DEFAULT_EXTENSIONS;
const ext = path.extname(filename);
return exts.includes(ext);
Expand Down Expand Up @@ -76,6 +91,20 @@ export function transform(
});
}

export function loadPartialConfig(opts: Object): Promise<Object> {
opts = {
...opts,
caller: CALLER,
};

return new Promise((resolve, reject) => {
babel.loadPartialConfig(opts, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
}

export function compile(
filename: string,
opts: Object | Function,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"lib",
"--copy-files",
"--only",
"src/foo/*",
"src/foo/*,src/README.md",
"--no-copy-ignored",
"--verbose"
]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extensions": [".tsx"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
x;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
x;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
x;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"args": ["src", "--out-dir", "lib"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"use strict";

x;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"use strict";

x;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Successfully compiled 2 files with Babel (123ms).
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ config <CWD>/.babelrc
programmatic options from @babel/cli
{
"sourceFileName": "../src/foo.js",
"filename": "src//foo.js",
"extensions": [
".js",
".jsx",
".es6",
".es",
".mjs"
],
"presets": [
"<ROOTDIR>//packages//babel-preset-react"
],
Expand All @@ -26,7 +34,6 @@ programmatic options from @babel/cli
],
"caller": {
"name": "@babel/cli"
},
"filename": "src//foo.js"
}
}
Successfully compiled 1 file with Babel (123ms).
13 changes: 10 additions & 3 deletions packages/babel-core/src/config/full.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import gensync, { type Handler } from "gensync";
import { forwardAsync } from "../gensync-utils/async";

import { mergeOptions } from "./util";
import * as context from "../index";
import * as babelContext from "../index";
import Plugin from "./plugin";
import { getItemDescriptor } from "./item";
import {
Expand Down Expand Up @@ -70,7 +70,7 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
return null;
}

const optionDefaults = {};
const optionDefaults: ValidatedOptions = {};

const { plugins, presets } = options;

Expand Down Expand Up @@ -161,6 +161,8 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
const opts: Object = optionDefaults;
mergeOptions(opts, options);

if (!isCompilableFile(context.filename, opts.extensions)) return null;
Copy link
Member Author

@nicolo-ribaudo nicolo-ribaudo Oct 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After #11907 is merged, I need to check how to make Babel report these files as "unsupported".
The we can use this info in @babel/cli/@babel/register instead of a custom ignore logic.

#11907 (comment)


yield* enhanceError(context, function* loadPluginDescriptors() {
pluginDescriptorsByPass[0].unshift(...initialPluginsDescriptors);

Expand Down Expand Up @@ -229,7 +231,7 @@ const loadDescriptor = makeWeakCache(function* (
let item = value;
if (typeof value === "function") {
const api = {
...context,
...babelContext,
...makeAPI(cache),
};
try {
Expand Down Expand Up @@ -395,3 +397,8 @@ function chain(a, b) {
}
};
}

function isCompilableFile(filename, extensions) {
if (filename == null) return true;
return extensions.some(ext => ext === "*" || filename.endsWith(ext));
}
10 changes: 9 additions & 1 deletion packages/babel-core/src/config/partial.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,19 @@ export default function* loadPrivatePartialConfig(
const configChain = yield* buildRootChain(args, context);
if (!configChain) return null;

const options = {};
const options: ValidatedOptions = {};
configChain.options.forEach(opts => {
mergeOptions(options, opts);
});

// If the programmatic options or config files don't set the "extensions"
// option, default to ["*"] for backward compatibility reasons.
// Note: this default value is set _before_ loading the presets, so it's
// safe to add the "extensions" option to a preset in a minor version.
// TODO(Babel 8): The default should be babel.DEFAULT_EXTENSIONS
// TODO: Use ??= once flow supports it.
options.extensions = options.extensions ?? ["*"];

// Tack the passes onto the object itself so that, if this object is
// passed back to Babel a second time, it will be in the right structure
// to not change behavior.
Expand Down
9 changes: 9 additions & 0 deletions packages/babel-core/src/config/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

import type { ValidatedOptions } from "./validation/options";

function unique<T>(...args: T[]): T[] {
return Array.from(new Set(args));
}

export function mergeOptions(
target: ValidatedOptions,
source: ValidatedOptions,
Expand All @@ -15,6 +19,11 @@ export function mergeOptions(
const generatorOpts = source.generatorOpts;
const targetObj = (target.generatorOpts = target.generatorOpts || {});
mergeDefaultFields(targetObj, generatorOpts);
} else if (k === "extensions" && target.extensions && source.extensions) {
target.extensions = unique(
...(target.extensions ?? []),
...source.extensions,
);
} else {
const val = source[k];
if (val !== undefined) target[k] = (val: any);
Expand Down
46 changes: 34 additions & 12 deletions packages/babel-core/src/config/validation/option-assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import type {
ConfigFileSearch,
BabelrcSearch,
IgnoreList,
FileExtension,
IgnoreItem,
PluginList,
PluginItem,
Expand Down Expand Up @@ -217,17 +217,20 @@ export function assertArray(
return value;
}

export function assertIgnoreList(
loc: OptionPath,
value: mixed,
): IgnoreList | void {
const arr = assertArray(loc, value);
if (arr) {
arr.forEach((item, i) => assertIgnoreItem(access(loc, i), item));
}
return (arr: any);
function createArrayAssertion<T>(
assertValid: (loc: GeneralPath, value: mixed) => T,
): (loc: GeneralPath, value: mixed) => $ReadOnlyArray<T> | void {
return (loc, value) => {
const arr = assertArray(loc, value);
if (arr) arr.forEach((item, i) => assertValid(access(loc, i), item));
return (arr: any);
};
}
function assertIgnoreItem(loc: GeneralPath, value: mixed): IgnoreItem {

export const assertIgnoreList = createArrayAssertion(function assertIgnoreItem(
loc: GeneralPath,
value: mixed,
): IgnoreItem {
if (
typeof value !== "string" &&
typeof value !== "function" &&
Expand All @@ -240,7 +243,26 @@ function assertIgnoreItem(loc: GeneralPath, value: mixed): IgnoreItem {
);
}
return value;
}
});

export const assertExtensionsList = createArrayAssertion(
function assertExtension(
loc: GeneralPath,
value: mixed,
): FileExtension | "*" {
if (value === "*") return value;

if (typeof value !== "string") {
throw new Error(
`${msg(loc)} must be an array of string values, or undefined`,
);
}
if (!value.startsWith(".")) {
throw new Error(`${msg(loc)} must start with a '.' (dot)`);
}
return (value: any);
},
);

export function assertConfigApplicableTest(
loc: OptionPath,
Expand Down
13 changes: 12 additions & 1 deletion packages/babel-core/src/config/validation/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
assertCallerMetadata,
assertInputSourceMap,
assertIgnoreList,
assertExtensionsList,
assertPluginList,
assertConfigApplicableTest,
assertConfigFileSearch,
Expand Down Expand Up @@ -114,6 +115,10 @@ const COMMON_VALIDATORS: ValidatorSet = {
$PropertyType<ValidatedOptions, "exclude">,
>),

extensions: (assertExtensionsList: Validator<
$PropertyType<ValidatedOptions, "extensions">,
>),

retainLines: (assertBoolean: Validator<
$PropertyType<ValidatedOptions, "retainLines">,
>),
Expand Down Expand Up @@ -195,9 +200,12 @@ export type ValidatedOptions = {

extends?: string,
env?: EnvSet<ValidatedOptions>,
overrides?: OverridesList,

// Options to enable/disable processing of some files
ignore?: IgnoreList,
only?: IgnoreList,
overrides?: OverridesList,
extensions?: ExtensionsList,

// Generally verify if a given config object should be applied to the given file.
test?: ConfigApplicableTest,
Expand Down Expand Up @@ -252,6 +260,9 @@ export type EnvSet<T> = {
export type IgnoreItem = string | Function | RegExp;
export type IgnoreList = $ReadOnlyArray<IgnoreItem>;

export opaque type FileExtension = string;
export type ExtensionsList = $ReadOnlyArray<FileExtension | "*">;

export type PluginOptions = {} | void | false;
export type PluginTarget = string | {} | Function;
export type PluginItem =
Expand Down
12 changes: 5 additions & 7 deletions packages/babel-core/src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// @flow

import type { ExtensionsList } from "./config/validation/options";

export { default as File } from "./transformation/file/file";
export { default as buildExternalHelpers } from "./tools/build-external-helpers";
export { resolvePlugin, resolvePreset } from "./config/files";
Expand Down Expand Up @@ -41,13 +43,9 @@ export { parse, parseSync, parseAsync } from "./parse";
* Recommended set of compilable extensions. Not used in @babel/core directly, but meant as
* as an easy source for tooling making use of @babel/core.
*/
export const DEFAULT_EXTENSIONS = Object.freeze([
".js",
".jsx",
".es6",
".es",
".mjs",
]);
export const DEFAULT_EXTENSIONS: ExtensionsList = Object.freeze(
([".js", ".jsx", ".es6", ".es", ".mjs"]: any),
);

// For easier backward-compatibility, provide an API like the one we exposed in Babel 6.
import { loadOptions } from "./config";
Expand Down
Loading