mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-09-30 17:27:22 +08:00
http://crrev.com/c/5898986 is currently failing presubmit on win bot because command line command for clang format gets too long. It looks like `git cl format` is running for all changes files ignoring the .clang-format Disable-Format directive in node_modules/ Bug: 370734442 Change-Id: Ia790c5998a4d25b42f2f14efc56df26cefa540f5 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/5907633 Commit-Queue: Danil Somsikov <dsv@chromium.org> Reviewed-by: Simon Zünd <szuend@chromium.org>
540 lines
18 KiB
JavaScript
540 lines
18 KiB
JavaScript
'use strict';
|
|
|
|
var yargs = require('yargs');
|
|
var fs = require('fs');
|
|
var path = require('path');
|
|
var transformAnalyzerResult = require('./chunk-transform-analyzer-result-jzWAvD19.js');
|
|
var fastGlob = require('fast-glob');
|
|
var tsModule = require('typescript');
|
|
require('ts-simple-type');
|
|
|
|
function _interopNamespaceDefault(e) {
|
|
var n = Object.create(null);
|
|
if (e) {
|
|
Object.keys(e).forEach(function (k) {
|
|
if (k !== 'default') {
|
|
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
Object.defineProperty(n, k, d.get ? d : {
|
|
enumerable: true,
|
|
get: function () { return e[k]; }
|
|
});
|
|
}
|
|
});
|
|
}
|
|
n.default = e;
|
|
return Object.freeze(n);
|
|
}
|
|
|
|
var yargs__namespace = /*#__PURE__*/_interopNamespaceDefault(yargs);
|
|
|
|
/**
|
|
* The most general version of compiler options.
|
|
*/
|
|
const defaultOptions = {
|
|
noEmitOnError: false,
|
|
allowJs: true,
|
|
maxNodeModuleJsDepth: 3,
|
|
experimentalDecorators: true,
|
|
target: tsModule.ScriptTarget.Latest,
|
|
downlevelIteration: true,
|
|
module: tsModule.ModuleKind.ESNext,
|
|
//module: ModuleKind.CommonJS,
|
|
//lib: ["ESNext", "DOM", "DOM.Iterable"],
|
|
strictNullChecks: true,
|
|
moduleResolution: tsModule.ModuleResolutionKind.NodeJs,
|
|
esModuleInterop: true,
|
|
noEmit: true,
|
|
allowSyntheticDefaultImports: true,
|
|
allowUnreachableCode: true,
|
|
allowUnusedLabels: true,
|
|
skipLibCheck: true
|
|
};
|
|
/**
|
|
* Compiles an array of file paths using typescript.
|
|
* @param filePaths
|
|
* @param options
|
|
*/
|
|
function compileTypescript(filePaths, options = defaultOptions) {
|
|
filePaths = Array.isArray(filePaths) ? filePaths : [filePaths];
|
|
const program = tsModule.createProgram(filePaths, options);
|
|
const files = program
|
|
.getSourceFiles()
|
|
.filter(sf => filePaths.includes(sf.fileName))
|
|
.sort((sfA, sfB) => (sfA.fileName > sfB.fileName ? 1 : -1));
|
|
return { program, files };
|
|
}
|
|
|
|
/**
|
|
* Logs to the console with a specific level.
|
|
* This function takes the config into account
|
|
* @param text
|
|
* @param config
|
|
* @param level
|
|
*/
|
|
function log(text, config, level = "normal") {
|
|
// Never log if silent
|
|
if (config.silent) {
|
|
return;
|
|
}
|
|
// Never log verbose if verbose is not on
|
|
if (level === "verbose" && !config.verbose) {
|
|
return;
|
|
}
|
|
// "unpack" function
|
|
if (typeof text === "function") {
|
|
text = text();
|
|
}
|
|
// eslint-disable-next-line no-console
|
|
if (typeof text === "object") {
|
|
// eslint-disable-next-line no-console
|
|
console.dir(text, { depth: 10 });
|
|
}
|
|
else {
|
|
// eslint-disable-next-line no-console
|
|
console.log(text);
|
|
}
|
|
}
|
|
/**
|
|
* Logs only if verbose is set to true in the config
|
|
* @param text
|
|
* @param config
|
|
*/
|
|
function logVerbose(text, config) {
|
|
log(text, config, "verbose");
|
|
}
|
|
|
|
const IGNORE_GLOBS = ["**/node_modules/**", "**/web_modules/**"];
|
|
const DEFAULT_DIR_GLOB = "**/*.{js,jsx,ts,tsx}";
|
|
const DEFAULT_GLOBS = [DEFAULT_DIR_GLOB];
|
|
/**
|
|
* Parses and analyses all globs and calls some callbacks while doing it.
|
|
* @param globs
|
|
* @param config
|
|
* @param context
|
|
*/
|
|
async function analyzeGlobs(globs, config, context = {}) {
|
|
var _a, _b, _c;
|
|
// Set default glob
|
|
if (globs.length === 0) {
|
|
globs = DEFAULT_GLOBS;
|
|
}
|
|
// Expand the globs
|
|
const filePaths = await expandGlobs(globs, config);
|
|
logVerbose(() => filePaths, config);
|
|
// Callbacks
|
|
(_a = context.didExpandGlobs) === null || _a === void 0 ? void 0 : _a.call(context, filePaths);
|
|
(_b = context.willAnalyzeFiles) === null || _b === void 0 ? void 0 : _b.call(context, filePaths);
|
|
// Parse all the files with typescript
|
|
const { program, files } = compileTypescript(filePaths);
|
|
// Analyze each file with web component analyzer
|
|
const results = [];
|
|
for (const file of files) {
|
|
// Analyze
|
|
const result = transformAnalyzerResult.analyzeSourceFile(file, {
|
|
program,
|
|
verbose: config.verbose || false,
|
|
ts: config.ts,
|
|
config: {
|
|
features: config.features,
|
|
analyzeDependencies: config.analyzeDependencies,
|
|
analyzeDefaultLib: config.analyzeDefaultLibrary,
|
|
analyzeGlobalFeatures: config.analyzeGlobalFeatures,
|
|
analyzeAllDeclarations: config.format == "json2" // TODO: find a better way to construct the config
|
|
}
|
|
});
|
|
logVerbose(() => transformAnalyzerResult.stripTypescriptValues(result, program.getTypeChecker()), config);
|
|
// Callback
|
|
await ((_c = context.emitAnalyzedFile) === null || _c === void 0 ? void 0 : _c.call(context, file, result, { program }));
|
|
results.push(result);
|
|
}
|
|
return { program, files, results };
|
|
}
|
|
/**
|
|
* Expands the globs.
|
|
* @param globs
|
|
* @param config
|
|
*/
|
|
async function expandGlobs(globs, config) {
|
|
globs = Array.isArray(globs) ? globs : [globs];
|
|
const ignoreGlobs = (config === null || config === void 0 ? void 0 : config.discoverNodeModules) ? [] : IGNORE_GLOBS;
|
|
return transformAnalyzerResult.arrayFlat(await Promise.all(globs.map(g => {
|
|
try {
|
|
// Test if the glob points to a directory.
|
|
// If so, return the result of a new glob that searches for files in the directory excluding node_modules..
|
|
const dirExists = fs.existsSync(g) && fs.lstatSync(g).isDirectory();
|
|
if (dirExists) {
|
|
return fastGlob([fastGlobNormalize(`${g}/${DEFAULT_DIR_GLOB}`)], {
|
|
ignore: ignoreGlobs,
|
|
absolute: true,
|
|
followSymbolicLinks: false
|
|
});
|
|
}
|
|
}
|
|
catch (e) {
|
|
// the glob wasn't a directory
|
|
}
|
|
// Return the result of globbing
|
|
return fastGlob([fastGlobNormalize(g)], {
|
|
ignore: ignoreGlobs,
|
|
absolute: true,
|
|
followSymbolicLinks: false
|
|
});
|
|
})));
|
|
}
|
|
/**
|
|
* Fast glob recommends normalizing paths for windows, because fast glob expects a Unix-style path.
|
|
* Read more here: https://github.com/mrmlnc/fast-glob#how-to-write-patterns-on-windows
|
|
* @param glob
|
|
*/
|
|
function fastGlobNormalize(glob) {
|
|
return glob.replace(/\\/g, "/");
|
|
}
|
|
|
|
const ERROR_NAME = "CLIError";
|
|
/**
|
|
* Make an error of kind "CLIError"
|
|
* Use this function instead of subclassing Error because of problems after transpilation.
|
|
* @param message
|
|
*/
|
|
function makeCliError(message) {
|
|
const error = new Error(message);
|
|
error.name = ERROR_NAME;
|
|
return error;
|
|
}
|
|
/**
|
|
* Returns if an error is of kind "CLIError"
|
|
* @param error
|
|
*/
|
|
function isCliError(error) {
|
|
return error instanceof Error && error.name === ERROR_NAME;
|
|
}
|
|
|
|
function ensureDirSync(dir) {
|
|
try {
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
}
|
|
catch (err) {
|
|
if (err.code !== "EEXIST")
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Runs the analyze cli command.
|
|
* @param config
|
|
*/
|
|
const analyzeCliCommand = async (config) => {
|
|
var _a;
|
|
const inputGlobs = config.glob || [];
|
|
// Log warning for experimental json format
|
|
if (config.format === "json" || config.format === "json2" || ((_a = config.outFile) === null || _a === void 0 ? void 0 : _a.endsWith(".json"))) {
|
|
log(`
|
|
!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!
|
|
The custom-elements.json format is for experimental purposes. You can expect changes to this format.
|
|
Please follow and contribute to the discussion at:
|
|
- https://github.com/webcomponents/custom-elements-json
|
|
- https://github.com/w3c/webcomponents/issues/776
|
|
!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!
|
|
`, config);
|
|
}
|
|
// If no "out" is specified, output to console
|
|
const outStrategy = (() => {
|
|
if (config.outDir == null && config.outFile == null && config.outFiles == null) {
|
|
switch (config.format) {
|
|
case "json2":
|
|
// "json2" will need to output everything at once
|
|
return "console_bulk";
|
|
default:
|
|
return "console_stream";
|
|
}
|
|
}
|
|
return "file";
|
|
})();
|
|
// Give this context to the analyzer
|
|
const context = {
|
|
didExpandGlobs(filePaths) {
|
|
if (filePaths.length === 0) {
|
|
throw makeCliError(`Couldn't find any files to analyze.`);
|
|
}
|
|
},
|
|
willAnalyzeFiles(filePaths) {
|
|
log(`Web Component Analyzer analyzing ${filePaths.length} file${filePaths.length === 1 ? "" : "s"}...`, config);
|
|
},
|
|
emitAnalyzedFile(file, result, { program }) {
|
|
// Emit the transformed results as soon as possible if "outConsole" is on
|
|
if (outStrategy === "console_stream") {
|
|
if (result.componentDefinitions.length > 0) {
|
|
// Always use "console.log" when outputting the results
|
|
/* eslint-disable-next-line no-console */
|
|
console.log(transformResults(result, program, { ...config, cwd: config.cwd || process.cwd() }));
|
|
}
|
|
}
|
|
}
|
|
};
|
|
// Analyze, - all the magic happens in here
|
|
const { results, program } = await analyzeGlobs(inputGlobs, config, context);
|
|
const filteredResults = results.filter(result => { var _a; return result.componentDefinitions.length > 0 || result.globalFeatures != null || (((_a = result.declarations) === null || _a === void 0 ? void 0 : _a.length) || 0) > 0; });
|
|
// Write files to the file system
|
|
if (outStrategy === "console_bulk") {
|
|
// Always use "console.log" when outputting the results
|
|
/* eslint-disable-next-line no-console */
|
|
console.log(transformResults(filteredResults, program, { ...config, cwd: config.cwd || process.cwd() }));
|
|
}
|
|
else if (outStrategy === "file") {
|
|
// Build up a map of "filePath => result[]"
|
|
const outputResultMap = await distributeResultsIntoFiles(filteredResults, config);
|
|
// Write all results to corresponding paths
|
|
for (const [outputPath, results] of outputResultMap) {
|
|
if (outputPath != null) {
|
|
if (config.dry) {
|
|
const tagNames = transformAnalyzerResult.arrayFlat(results.map(result => result.componentDefinitions.map(d => d.tagName)));
|
|
log(`[dry] Intending to write ${tagNames} to ./${path.relative(process.cwd(), outputPath)}`, config);
|
|
}
|
|
else {
|
|
const content = transformResults(results, program, { ...config, cwd: config.cwd || path.dirname(outputPath) });
|
|
ensureDirSync(path.dirname(outputPath));
|
|
fs.writeFileSync(outputPath, content);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
/**
|
|
* Transforms analyze results based on the wca cli config.
|
|
* @param results
|
|
* @param program
|
|
* @param config
|
|
*/
|
|
function transformResults(results, program, config) {
|
|
var _a, _b;
|
|
results = Array.isArray(results) ? results : [results];
|
|
// Default format is "markdown"
|
|
const format = config.format || "markdown";
|
|
const transformerConfig = {
|
|
inlineTypes: (_a = config.inlineTypes) !== null && _a !== void 0 ? _a : false,
|
|
visibility: (_b = config.visibility) !== null && _b !== void 0 ? _b : "public",
|
|
markdown: config.markdown,
|
|
cwd: config.cwd
|
|
};
|
|
return transformAnalyzerResult.transformAnalyzerResult(format, results, program, transformerConfig);
|
|
}
|
|
/**
|
|
* Analyzes input globs and returns the transformed result.
|
|
* @param inputGlobs
|
|
* @param config
|
|
*/
|
|
async function analyzeAndTransformGlobs(inputGlobs, config) {
|
|
const { results, program } = await analyzeGlobs(Array.isArray(inputGlobs) ? inputGlobs : [inputGlobs], config);
|
|
return transformResults(results, program, config);
|
|
}
|
|
/**
|
|
* Distribute results into files and return a map of "path => results"
|
|
* @param results
|
|
* @param config
|
|
*/
|
|
async function distributeResultsIntoFiles(results, config) {
|
|
const outputPathToResultMap = new Map();
|
|
// Helper function to add a result to a path. It will merge into existing results.
|
|
const addToOutputPath = (path, result) => {
|
|
const existing = outputPathToResultMap.get(path) || [];
|
|
existing.push(result);
|
|
outputPathToResultMap.set(path, existing);
|
|
};
|
|
// Output files into directory
|
|
if (config.outDir != null) {
|
|
// Get extension name based on the specified format.
|
|
const extName = formatToExtension(config.format || "markdown");
|
|
for (const result of results) {
|
|
// Write file to disc for each analyzed file
|
|
const definition = result.componentDefinitions[0];
|
|
if (definition == null)
|
|
continue;
|
|
// The name of the file becomes the tagName of the first component definition in the file.
|
|
const path$1 = path.resolve(process.cwd(), config.outDir, `${definition.tagName}${extName}`);
|
|
addToOutputPath(path$1, result);
|
|
}
|
|
}
|
|
// Output all results into a single file
|
|
else if (config.outFile != null) {
|
|
// Guess format based on outFile extension
|
|
// eslint-disable-next-line require-atomic-updates
|
|
config.format = config.format || extensionToFormat(config.outFile);
|
|
const path$1 = path.resolve(process.cwd(), config.outFile);
|
|
for (const result of results) {
|
|
addToOutputPath(path$1, result);
|
|
}
|
|
}
|
|
// Output all results into multiple files
|
|
else if (config.outFiles != null) {
|
|
// Guess format based on outFile extension
|
|
// eslint-disable-next-line require-atomic-updates
|
|
config.format = config.format || extensionToFormat(config.outFiles);
|
|
for (const result of results) {
|
|
const dir = path.relative(process.cwd(), path.dirname(result.sourceFile.fileName));
|
|
const filename = path.relative(process.cwd(), path.basename(result.sourceFile.fileName, path.extname(result.sourceFile.fileName)));
|
|
for (const definition of result.componentDefinitions) {
|
|
// The name of the file becomes the tagName of the first component definition in the file.
|
|
const path$1 = path.resolve(process.cwd(), config
|
|
.outFiles.replace(/{dir}/g, dir)
|
|
.replace(/{filename}/g, filename)
|
|
.replace(/{tagname}/g, definition.tagName));
|
|
//const path = resolve(process.cwd(), config.outFiles!, definition.tagName);
|
|
addToOutputPath(path$1, {
|
|
sourceFile: result.sourceFile,
|
|
componentDefinitions: [definition]
|
|
});
|
|
}
|
|
}
|
|
}
|
|
// Not "out" was specified. Add results to the special key "null"
|
|
else {
|
|
outputPathToResultMap.set(null, results);
|
|
}
|
|
return outputPathToResultMap;
|
|
}
|
|
/**
|
|
* Returns an extension based on a format
|
|
* @param kind
|
|
*/
|
|
function formatToExtension(kind) {
|
|
switch (kind) {
|
|
case "json":
|
|
case "vscode":
|
|
return ".json";
|
|
case "md":
|
|
case "markdown":
|
|
return ".md";
|
|
default:
|
|
return ".txt";
|
|
}
|
|
}
|
|
/**
|
|
* Returns a format based on an extension
|
|
* @param path
|
|
*/
|
|
function extensionToFormat(path$1) {
|
|
const extName = path.extname(path$1);
|
|
switch (extName) {
|
|
case ".json":
|
|
return "json";
|
|
case ".md":
|
|
return "markdown";
|
|
default:
|
|
return "markdown";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The main function of the cli.
|
|
*/
|
|
function cli() {
|
|
const argv = yargs__namespace
|
|
.usage("Usage: $0 <command> [glob..] [options]")
|
|
.command({
|
|
command: ["analyze [glob..]", "$0"],
|
|
describe: "Analyses components and emits results in a specified format.",
|
|
handler: async (config) => {
|
|
try {
|
|
await analyzeCliCommand(config);
|
|
}
|
|
catch (e) {
|
|
if (isCliError(e)) {
|
|
log(e.message, config);
|
|
}
|
|
else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
})
|
|
.example(`$ $0 analyze`, "")
|
|
.example(`$ $0 analyze src --format markdown`, "")
|
|
.example(`$ $0 analyze "src/**/*.{js,ts}" --outDir output`, "")
|
|
.example(`$ $0 analyze my-element.js --outFile custom-elements.json`, "")
|
|
.example(`$ $0 analyze --outFiles {dir}/custom-element.json`, "")
|
|
.option("outDir", {
|
|
describe: `Output to a directory where each file corresponds to a web component`,
|
|
nargs: 1,
|
|
string: true
|
|
})
|
|
.option("outFile", {
|
|
describe: `Concatenate and emit output to a single file`,
|
|
nargs: 1,
|
|
string: true
|
|
})
|
|
.option("outFiles", {
|
|
describe: `Emit output to multiple files using a pattern. Available substitutions:
|
|
o {dir}: The directory of the component
|
|
o {filename}: The filename (without ext) of the component
|
|
o {tagname}: The element's tag name`,
|
|
nargs: 1,
|
|
string: true
|
|
})
|
|
.option("format", {
|
|
describe: `Specify output format`,
|
|
choices: ["md", "markdown", "json", "json2", "vscode"],
|
|
nargs: 1,
|
|
alias: "f"
|
|
})
|
|
.option("features", {
|
|
describe: `Features to enable`,
|
|
array: true,
|
|
choices: ["member", "method", "cssproperty", "csspart", "event", "slot"]
|
|
})
|
|
.option("analyzeDefaultLibrary", {
|
|
boolean: true,
|
|
hidden: true
|
|
})
|
|
.option("analyzeDependencies", {
|
|
boolean: true,
|
|
hidden: true
|
|
})
|
|
.option("discoverNodeModules", {
|
|
boolean: true,
|
|
hidden: true
|
|
})
|
|
.option("visibility", {
|
|
describe: `Minimum visibility`,
|
|
choices: ["private", "protected", "public"]
|
|
})
|
|
.option("inlineTypes", {
|
|
describe: `Inline type aliases`,
|
|
boolean: true
|
|
})
|
|
.option("dry", {
|
|
describe: `Don't write files`,
|
|
boolean: true,
|
|
alias: "d"
|
|
})
|
|
.option("verbose", {
|
|
boolean: true,
|
|
hidden: true
|
|
})
|
|
.option("silent", {
|
|
boolean: true,
|
|
hidden: true
|
|
})
|
|
// This options makes it possible to use "markdown.<sub-option>" in "strict mode"
|
|
.option("markdown", {
|
|
hidden: true
|
|
})
|
|
// This option makes it possible to specify a base cwd to use when emitting paths
|
|
.option("cwd", {
|
|
string: true,
|
|
hidden: true
|
|
})
|
|
.alias("v", "version")
|
|
.help("h")
|
|
.wrap(110)
|
|
.strict()
|
|
.alias("h", "help").argv;
|
|
if (argv.verbose) {
|
|
/* eslint-disable-next-line no-console */
|
|
console.log("CLI options:", argv);
|
|
}
|
|
}
|
|
|
|
exports.analyzeAndTransformGlobs = analyzeAndTransformGlobs;
|
|
exports.analyzeCliCommand = analyzeCliCommand;
|
|
exports.cli = cli;
|