mirror of
https://github.com/github/codeql-action.git
synced 2025-12-28 02:00:12 +08:00
Co-authored-by: Andrew Eisenberg <aeisenberg@github.com> Co-authored-by: Henry Mercer <henrymercer@github.com>
306 lines
12 KiB
JavaScript
306 lines
12 KiB
JavaScript
"use strict";
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
}) : (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
o[k2] = m[k];
|
|
}));
|
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
}) : function(o, v) {
|
|
o["default"] = v;
|
|
});
|
|
var __importStar = (this && this.__importStar) || function (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
__setModuleDefault(result, mod);
|
|
return result;
|
|
};
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.Def = exports.deepGenerate = exports.Code = void 0;
|
|
const Node_1 = require("./Node");
|
|
const Import_1 = require("./Import");
|
|
const prettier_1 = __importStar(require("prettier"));
|
|
const parser_typescript_1 = __importDefault(require("prettier/parser-typescript"));
|
|
const is_plain_object_1 = require("./is-plain-object");
|
|
const ConditionalOutput_1 = require("./ConditionalOutput");
|
|
const index_1 = require("./index");
|
|
// We only have a single top-level Code.toStringWithImports running at a time,
|
|
// so use a global var to capture this contextual state.
|
|
let usedConditionals = [];
|
|
class Code extends Node_1.Node {
|
|
constructor(literals, placeholders) {
|
|
super();
|
|
this.literals = literals;
|
|
this.placeholders = placeholders;
|
|
// Used by joinCode
|
|
this.trim = false;
|
|
this.oneline = false;
|
|
}
|
|
/**
|
|
* Returns the code with any necessary import statements prefixed.
|
|
*
|
|
* This method will also use any local `.prettierrc` settings, hence needs
|
|
* to return a `Promise<String>`.
|
|
*/
|
|
toStringWithImports(opts) {
|
|
const { path = '', forceDefaultImport, forceModuleImport, prefix, prettierOverrides = {}, importMappings = {} } = opts || {};
|
|
const ourModulePath = path.replace(/\.[tj]sx?/, '');
|
|
if (forceDefaultImport || forceModuleImport) {
|
|
this.deepReplaceNamedImports(forceDefaultImport || [], forceModuleImport || []);
|
|
}
|
|
usedConditionals = this.deepConditionalOutput();
|
|
const imports = this.deepFindImports();
|
|
const defs = this.deepFindDefs();
|
|
assignAliasesIfNeeded(defs, imports, ourModulePath);
|
|
const importPart = Import_1.emitImports(imports, ourModulePath, importMappings);
|
|
const bodyPart = this.generateCode();
|
|
const maybePrefix = prefix ? `${prefix}\n` : '';
|
|
return maybePrettyWithConfig(maybePrefix + importPart + '\n' + bodyPart, prettierOverrides);
|
|
}
|
|
/**
|
|
* Returns the formatted code, without any imports.
|
|
*
|
|
* Note that we don't use `.prettierrc` b/c that requires async I/O to resolve.
|
|
*/
|
|
toString() {
|
|
return maybePretty(this.generateCode());
|
|
}
|
|
asOneline() {
|
|
this.oneline = true;
|
|
return this;
|
|
}
|
|
get childNodes() {
|
|
return this.placeholders;
|
|
}
|
|
toCodeString() {
|
|
return this.generateCode();
|
|
}
|
|
deepFindImports() {
|
|
const imports = [];
|
|
let todo = [this];
|
|
while (todo.length > 0) {
|
|
const placeholder = todo.shift();
|
|
if (placeholder instanceof Import_1.Import) {
|
|
imports.push(placeholder);
|
|
}
|
|
else if (placeholder instanceof Node_1.Node) {
|
|
todo = [...todo, ...placeholder.childNodes];
|
|
}
|
|
else if (placeholder instanceof ConditionalOutput_1.MaybeOutput) {
|
|
if (usedConditionals.includes(placeholder.parent)) {
|
|
todo = [...todo, placeholder.code];
|
|
}
|
|
}
|
|
else if (Array.isArray(placeholder)) {
|
|
todo = [...todo, ...placeholder];
|
|
}
|
|
}
|
|
return imports;
|
|
}
|
|
deepFindDefs() {
|
|
const defs = [];
|
|
let todo = [this];
|
|
while (todo.length > 0) {
|
|
const placeholder = todo.shift();
|
|
if (placeholder instanceof Def) {
|
|
defs.push(placeholder);
|
|
}
|
|
else if (placeholder instanceof Node_1.Node) {
|
|
todo = [...todo, ...placeholder.childNodes];
|
|
}
|
|
else if (placeholder instanceof ConditionalOutput_1.MaybeOutput) {
|
|
if (usedConditionals.includes(placeholder.parent)) {
|
|
todo = [...todo, placeholder.code];
|
|
}
|
|
}
|
|
else if (Array.isArray(placeholder)) {
|
|
todo = [...todo, ...placeholder];
|
|
}
|
|
}
|
|
return defs;
|
|
}
|
|
deepConditionalOutput() {
|
|
const used = [];
|
|
let todo = [this];
|
|
while (todo.length > 0) {
|
|
const placeholder = todo.shift();
|
|
if (placeholder instanceof ConditionalOutput_1.ConditionalOutput) {
|
|
used.push(placeholder);
|
|
todo = [...todo, ...placeholder.declarationSiteCode.childNodes];
|
|
}
|
|
else if (placeholder instanceof Node_1.Node) {
|
|
todo = [...todo, ...placeholder.childNodes];
|
|
}
|
|
else if (Array.isArray(placeholder)) {
|
|
todo = [...todo, ...placeholder];
|
|
}
|
|
}
|
|
return used;
|
|
}
|
|
deepReplaceNamedImports(forceDefaultImport, forceModuleImport) {
|
|
// Keep a map of module name --> symbol we're importing, i.e. protobufjs/simple is _m1
|
|
const assignedNames = {};
|
|
function getName(source) {
|
|
let name = assignedNames[source];
|
|
if (!name) {
|
|
name = `_m${Object.values(assignedNames).length}`;
|
|
assignedNames[source] = name;
|
|
}
|
|
return name;
|
|
}
|
|
let todo = [this];
|
|
while (todo.length > 0) {
|
|
const placeholder = todo.shift();
|
|
if (placeholder instanceof Node_1.Node) {
|
|
const array = placeholder.childNodes;
|
|
for (let i = 0; i < array.length; i++) {
|
|
const maybeImp = array[i];
|
|
if (maybeImp instanceof Import_1.ImportsName && forceDefaultImport.includes(maybeImp.source)) {
|
|
const name = getName(maybeImp.source);
|
|
array[i] = index_1.code `${new Import_1.ImportsDefault(name, maybeImp.source)}.${maybeImp.sourceSymbol || maybeImp.symbol}`;
|
|
}
|
|
else if (maybeImp instanceof Import_1.ImportsName && forceModuleImport.includes(maybeImp.source)) {
|
|
const name = getName(maybeImp.source);
|
|
array[i] = index_1.code `${new Import_1.ImportsAll(name, maybeImp.source)}.${maybeImp.sourceSymbol || maybeImp.symbol}`;
|
|
}
|
|
}
|
|
todo = [...todo, ...placeholder.childNodes];
|
|
}
|
|
else if (Array.isArray(placeholder)) {
|
|
todo = [...todo, ...placeholder];
|
|
}
|
|
}
|
|
}
|
|
generateCode() {
|
|
const { literals, placeholders } = this;
|
|
let result = '';
|
|
// interleave the literals with the placeholders
|
|
for (let i = 0; i < placeholders.length; i++) {
|
|
result += literals[i] + deepGenerate(placeholders[i]);
|
|
}
|
|
// add the last literal
|
|
result += literals[literals.length - 1];
|
|
if (this.trim) {
|
|
result = result.trim();
|
|
}
|
|
if (this.oneline) {
|
|
result = result.replace(/\n/g, '');
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
exports.Code = Code;
|
|
function deepGenerate(object) {
|
|
let result = '';
|
|
let todo = [object];
|
|
while (todo.length > 0) {
|
|
const current = todo.shift();
|
|
if (Array.isArray(current)) {
|
|
todo = [...todo, ...current];
|
|
}
|
|
else if (current instanceof Node_1.Node) {
|
|
result += current.toCodeString();
|
|
}
|
|
else if (current instanceof ConditionalOutput_1.MaybeOutput) {
|
|
if (usedConditionals.includes(current.parent)) {
|
|
result += current.code.toCodeString();
|
|
}
|
|
}
|
|
else if (current === null) {
|
|
result += 'null';
|
|
}
|
|
else if (current !== undefined) {
|
|
if (is_plain_object_1.isPlainObject(current)) {
|
|
result += JSON.stringify(current);
|
|
}
|
|
else {
|
|
result += current.toString();
|
|
}
|
|
}
|
|
else {
|
|
result += 'undefined';
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
exports.deepGenerate = deepGenerate;
|
|
// Use an optional call here in case we are using standalone prettier. This can happen when loaded through a CDN from
|
|
// a browser (or Deno), because prettier has `"browser": "./standalone.js"` in it's package.json.
|
|
const configPromise = prettier_1.resolveConfig === null || prettier_1.resolveConfig === void 0 ? void 0 : prettier_1.resolveConfig('./');
|
|
async function maybePrettyWithConfig(input, options) {
|
|
try {
|
|
const config = await configPromise;
|
|
return prettier_1.default.format(input.trim(), { parser: 'typescript', plugins: [parser_typescript_1.default], ...config, ...options });
|
|
}
|
|
catch (e) {
|
|
return input; // assume it's invalid syntax and ignore
|
|
}
|
|
}
|
|
/** Finds any namespace collisions of a named import colliding with def and assigns the import an alias it. */
|
|
function assignAliasesIfNeeded(defs, imports, ourModulePath) {
|
|
// Keep track of used (whether declared or imported) symbols
|
|
const usedSymbols = new Set();
|
|
// Mark all locally-defined symbols as used
|
|
defs.forEach((def) => usedSymbols.add(def.symbol));
|
|
// A mapping of original to assigned alias, i.e. Foo@foo --> Foo2
|
|
const assignedAliases = {};
|
|
let j = 1;
|
|
imports.forEach((i) => {
|
|
if (i instanceof Import_1.ImportsName &&
|
|
// Don't both aliasing imports from our own module
|
|
!(Import_1.sameModule(i.source, ourModulePath) || (i.definedIn && Import_1.sameModule(i.definedIn, ourModulePath)))) {
|
|
const key = `${i.symbol}@${i.source}`;
|
|
if (usedSymbols.has(i.symbol)) {
|
|
let alias = assignedAliases[key];
|
|
if (!alias) {
|
|
alias = `${i.symbol}${j++}`;
|
|
assignedAliases[key] = alias;
|
|
}
|
|
// Move the original symbol over
|
|
if (alias !== i.symbol) {
|
|
i.sourceSymbol = i.symbol;
|
|
}
|
|
i.symbol = alias;
|
|
}
|
|
else {
|
|
usedSymbols.add(i.symbol);
|
|
assignedAliases[key] = i.symbol;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
function maybePretty(input) {
|
|
try {
|
|
return prettier_1.default.format(input.trim(), { parser: 'typescript', plugins: [parser_typescript_1.default] });
|
|
}
|
|
catch (e) {
|
|
return input; // assume it's invalid syntax and ignore
|
|
}
|
|
}
|
|
/**
|
|
* Represents a symbol defined in the current file.
|
|
*
|
|
* We use this to know if a symbol imported from a different file is going to
|
|
* have a namespace collision.
|
|
*/
|
|
class Def extends Node_1.Node {
|
|
constructor(symbol) {
|
|
super();
|
|
this.symbol = symbol;
|
|
}
|
|
toCodeString() {
|
|
return this.symbol;
|
|
}
|
|
/** Any potentially string/SymbolSpec/Code nested nodes within us. */
|
|
get childNodes() {
|
|
return [];
|
|
}
|
|
}
|
|
exports.Def = Def;
|