Files
test-repo/src/main/parser.js
T
fisker Cheung a21dfbfa21 Merge branch 'main' into next-rebase
# Conflicts:
#	.eslintrc.cjs
#	package.json
#	scripts/build/bundler.mjs
#	scripts/build/config.mjs
#	scripts/build/esbuild-plugins/replace-module.mjs
#	src/cli/file-info.js
#	src/cli/find-config-path.js
#	src/cli/index.js
2022-03-24 15:02:29 +08:00

118 lines
2.9 KiB
JavaScript

import { createRequire } from "node:module";
import path from "node:path";
import { ConfigError } from "../common/errors.js";
import { locStart, locEnd } from "../language-js/loc.js";
const require = createRequire(import.meta.url);
// Use defineProperties()/getOwnPropertyDescriptor() to prevent
// triggering the parsers getters.
const ownNames = Object.getOwnPropertyNames;
const ownDescriptor = Object.getOwnPropertyDescriptor;
function getParsers(options) {
const parsers = {};
for (const plugin of options.plugins) {
// TODO: test this with plugins
/* istanbul ignore next */
if (!plugin.parsers) {
continue;
}
for (const name of ownNames(plugin.parsers)) {
Object.defineProperty(parsers, name, ownDescriptor(plugin.parsers, name));
}
}
return parsers;
}
function resolveParser(opts, parsers = getParsers(opts)) {
if (typeof opts.parser === "function") {
// Custom parser API always works with JavaScript.
return {
parse: opts.parser,
astFormat: "estree",
locStart,
locEnd,
};
}
if (typeof opts.parser === "string") {
if (Object.prototype.hasOwnProperty.call(parsers, opts.parser)) {
return parsers[opts.parser];
}
/* istanbul ignore next */
if (process.env.PRETTIER_TARGET === "universal") {
throw new ConfigError(
`Couldn't resolve parser "${opts.parser}". Parsers must be explicitly added to the standalone bundle.`
);
}
// This line of code will be removed when bundling `standalone.js`
return requireParser(opts.parser);
}
}
function requireParser(parser) {
try {
return {
parse: require(path.resolve(process.cwd(), parser)),
astFormat: "estree",
locStart,
locEnd,
};
} catch {
/* istanbul ignore next */
throw new ConfigError(`Couldn't resolve parser "${parser}"`);
}
}
function parse(text, opts) {
const parsers = getParsers(opts);
// Create a new object {parserName: parseFn}. Uses defineProperty() to only call
// the parsers getters when actually calling the parser `parse` function.
const parsersForCustomParserApi = Object.defineProperties(
{},
Object.fromEntries(
Object.keys(parsers).map((parserName) => [
parserName,
{
enumerable: true,
get() {
return parsers[parserName].parse;
},
},
])
)
);
const parser = resolveParser(opts, parsers);
try {
if (parser.preprocess) {
text = parser.preprocess(text, opts);
}
return {
text,
ast: parser.parse(text, parsersForCustomParserApi, opts),
};
} catch (error) {
const { loc } = error;
if (loc) {
const { codeFrameColumns } = require("@babel/code-frame");
error.codeFrame = codeFrameColumns(text, loc, { highlightCode: true });
error.message += "\n" + error.codeFrame;
throw error;
}
/* istanbul ignore next */
throw error.stack;
}
}
export { parse, resolveParser };