"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`. */ 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;