initial commit of actions

This commit is contained in:
Dominik Polakovics Polakovics 2026-01-31 18:56:04 +01:00
commit 949ece5785
44660 changed files with 12034344 additions and 0 deletions

View file

@ -0,0 +1,61 @@
import { Node } from './Node';
import { Options } from 'prettier';
/** Options for `toStringWithImports`, i.e. for the top-level, per-file output. */
export interface ToStringOpts {
/** The intended file name of this code; used to know whether we can skip import statements that would be from our own file. */
path?: string;
/** Modules to use a CommonJS-in-ESM destructure fix for. */
forceDefaultImport?: string[];
/** Modules to use a CommonJS-in-ESM destructure fix for. */
forceModuleImport?: string[];
/** A top-of-file prefix, i.e. eslint disable. */
prefix?: string;
/** Optional per-file overrides for the prettier config, i.e. to use longer-than-normal line lengths. */
prettierOverrides?: Options;
/** optional importMappings */
importMappings?: {
[key: string]: string;
};
}
export declare class Code extends Node {
private literals;
private placeholders;
trim: boolean;
private oneline;
constructor(literals: TemplateStringsArray, placeholders: any[]);
/**
* 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?: ToStringOpts): Promise<string>;
/**
* Returns the formatted code, without any imports.
*
* Note that we don't use `.prettierrc` b/c that requires async I/O to resolve.
*/
toString(): string;
asOneline(): Code;
get childNodes(): unknown[];
toCodeString(): string;
private deepFindImports;
private deepFindDefs;
private deepConditionalOutput;
private deepReplaceNamedImports;
private generateCode;
}
export declare function deepGenerate(object: unknown): string;
/**
* 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.
*/
export declare class Def extends Node {
symbol: string;
constructor(symbol: string);
toCodeString(): string;
/** Any potentially string/SymbolSpec/Code nested nodes within us. */
get childNodes(): Node[];
}

View file

@ -0,0 +1,305 @@
"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;

View file

@ -0,0 +1,40 @@
import { Node } from './Node';
import { Code } from './Code';
/**
* Helps output conditional helper methods.
*
* The `ConditionalOutput` concept is split into a usage site and a declaration
* site, i.e. declaring a `function someHelper() { ... }`, and calling it
* like `someHelper()`.
*
* While generating code, you can make usage says by using `someHelper` as
* a placeholder, and then output the declaration with `someHelper.ifUsed`
* to output the declaration conditionally only if `someHelper` has been
* seen in the tree.
*
* ```typescript
* const someHelper = conditionalOutput(
* "someHelper",
* code`function someHelper() { return 1 } `
* );
*
* const code = code`
* ${someHelper}
*
* ${someHelper.ifUsed}
* `
* ```
*/
export declare class ConditionalOutput extends Node {
usageSiteName: string;
declarationSiteCode: Code;
constructor(usageSiteName: string, declarationSiteCode: Code);
get childNodes(): unknown[];
toCodeString(): string;
get ifUsed(): MaybeOutput;
}
export declare class MaybeOutput {
parent: ConditionalOutput;
code: Code;
constructor(parent: ConditionalOutput, code: Code);
}

View file

@ -0,0 +1,56 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MaybeOutput = exports.ConditionalOutput = void 0;
const Node_1 = require("./Node");
/**
* Helps output conditional helper methods.
*
* The `ConditionalOutput` concept is split into a usage site and a declaration
* site, i.e. declaring a `function someHelper() { ... }`, and calling it
* like `someHelper()`.
*
* While generating code, you can make usage says by using `someHelper` as
* a placeholder, and then output the declaration with `someHelper.ifUsed`
* to output the declaration conditionally only if `someHelper` has been
* seen in the tree.
*
* ```typescript
* const someHelper = conditionalOutput(
* "someHelper",
* code`function someHelper() { return 1 } `
* );
*
* const code = code`
* ${someHelper}
*
* ${someHelper.ifUsed}
* `
* ```
*/
class ConditionalOutput extends Node_1.Node {
// A given ConditionalOutput const could be used in multiple code
// parents, and so we don't want to use instance state to store
// "should I be output or not", b/c it depends on the containing tree.
constructor(usageSiteName, declarationSiteCode) {
super();
this.usageSiteName = usageSiteName;
this.declarationSiteCode = declarationSiteCode;
}
get childNodes() {
return [this.declarationSiteCode];
}
toCodeString() {
return this.usageSiteName;
}
get ifUsed() {
return new MaybeOutput(this, this.declarationSiteCode);
}
}
exports.ConditionalOutput = ConditionalOutput;
class MaybeOutput {
constructor(parent, code) {
this.parent = parent;
this.code = code;
}
}
exports.MaybeOutput = MaybeOutput;

View file

@ -0,0 +1,190 @@
import { Node } from './Node';
export declare const importType = "[*@+=]";
/**
* Specifies a symbol and its related origin, either via import or implicit/local declaration.
*/
export declare abstract class Import extends Node {
symbol: string;
/**
* Parses a symbol reference pattern to create a symbol. The pattern
* allows the simple definition of all symbol types including any possible
* import variation. If the spec to parse does not follow the proper format
* an implicit symbol is created from the unparsed spec.
*
* Pattern: `symbolName? importType modulePath (#<augmentedSymbolName>)?`
*
* Where:
*
* - `symbolName` is any legal JS/TS symbol. If none, we use the last part of the module path as a guess.
* - `importType` is one of `@` or `*` or `+`, where:
* - `@` is a named import
* - `Foo@bar` becomes `import { Foo } from 'bar'`
* - `*` is a star import,
* - `*Foo` becomes `import * as Foo from 'Foo'`
* - `Foo*foo` becomes `import * as Foo from 'foo'`
* - `+` is an implicit import
* - E.g. `Foo+foo` becomes `import 'foo'`
* - `modulePath` is a path
* - E.g. `<filename>(/<filename)*`
* - augmentedSymbolName = `[a-zA-Z0-9_]+`
*
* Any valid symbol name that represents the symbol that is being augmented. For example,
* the import `rxjs/add/observable/from` attaches the `from` method to the `Observable` class.
* To import it correctly the spec should be `+rxjs/add/observable/from#Observable`. Adding this
* parameter to augmented imports ensures they are output only when the symbol being augmented
* is actually used.
*
*
* @param spec Symbol spec to parse.
* @return Parsed symbol specification
*/
static from(spec: string): Import;
static fromMaybeString(spec: string | Import): Import;
/**
* Defined if the symbol is typically imported from a barrel/etc. path, but is technically defined in another file.
*
* We need to know this in case the "this comes from the barrel" type ends up being used in the file where
* the symbol itself is defined, i.e. we don't need an import in that case.
*/
definedIn?: string;
protected constructor(symbol: string);
toCodeString(): string;
get childNodes(): unknown[];
abstract source: string | undefined;
/**
* Creates an import of all the modules exported symbols as a single
* local named symbol
*
* e.g. `import * as Engine from 'templates';`
*
* @param localName The local name of the imported symbols
* @param from The module to import the symbols from
*/
static importsAll(localName: string, from: string): Import;
/**
* Creates an import of a single named symbol from the module's exported
* symbols.
*
* e.g. `import { Engine } from 'templates';`
*
* @param exportedName The symbol that is both exported and imported
* @param from The module the symbol is exported from
* @param typeImport whether this is an `import type` import
*/
static importsName(exportedName: string, from: string, typeImport: boolean, sourceExportedName?: string): Import;
/**
* Creates a symbol that is brought in by a whole module import
* that "augments" an existing symbol.
*
* e.g. `import 'rxjs/add/operator/flatMap'`
*
* @param symbolName The augmented symbol to be imported
* @param from The entire import that does the augmentation
* @param target The symbol that is augmented
*/
static augmented(symbolName: string, from: string, target: string): Import;
/**
* Creates a symbol that is brought in as a side effect of
* an import.
*
* e.g. `import 'mocha'`
*
* @param symbolName The symbol to be imported
* @param from The entire import that does the augmentation
*/
static sideEffect(symbolName: string, from: string): Import;
/**
* An implied symbol that does no tracking of imports
*
* @param name The implicit symbol name
*/
static implicit(name: string): Import;
/**
* Creates an import of a single named symbol from the module's exported
* default.
*
* e.g. `import Engine from 'engine';`
*
* @param exportedName The symbol that is both exported and imported
* @param from The module the symbol is exported from
*/
static importsDefault(exportedName: string, from: string): Import;
}
/**
* Non-imported symbol
*/
export declare class Implicit extends Import {
constructor(symbol: string);
source: undefined;
}
/** Common base class for imported symbols. */
export declare abstract class Imported extends Import {
symbol: string;
source: string;
/** The symbol is the imported symbol, i.e. `BarClass`, and source is the path it comes from. */
protected constructor(symbol: string, source: string);
}
/**
* Imports a single named symbol from the module's exported
* symbols.
*
* E.g.:
*
* `import { Engine } from 'templates'` or
* `import { Engine as Engine2 } from 'templates'`
*/
export declare class ImportsName extends Imported {
sourceSymbol?: string | undefined;
typeImport?: boolean | undefined;
/**
* @param symbol
* @param source
* @param sourceSymbol is the optional original symbol, i.e if we're renaming the symbol it is `Engine`
* @param typeImport whether this is an `import type` import
*/
constructor(symbol: string, source: string, sourceSymbol?: string | undefined, typeImport?: boolean | undefined);
toImportPiece(): string;
}
/**
* Imports a single named symbol from the module's exported
* default.
*
* e.g. `import Engine from 'engine';`
*/
export declare class ImportsDefault extends Imported {
constructor(symbol: string, source: string);
}
/**
* Imports all of the modules exported symbols as a single
* named symbol
*
* e.g. `import * as Engine from 'templates';`
*/
export declare class ImportsAll extends Imported {
constructor(symbol: string, source: string);
}
/**
* A symbol that is brought in by a whole module import
* that "augments" an existing symbol.
*
* e.g. `import 'rxjs/add/operator/flatMap'`
*/
export declare class Augmented extends Imported {
augmented: string;
constructor(symbol: string, source: string, augmented: string);
}
/**
* A symbol that is brought in as a side effect of an import.
*
* E.g. `from("Foo+mocha")` will add `import 'mocha'`
*/
export declare class SideEffect extends Imported {
constructor(symbol: string, source: string);
}
/** Generates the `import ...` lines for the given `imports`. */
export declare function emitImports(imports: Import[], ourModulePath: string, importMappings: {
[key: string]: string;
}): string;
export declare function maybeRelativePath(outputPath: string, importPath: string): string;
/** Checks if `path1 === path2` despite minor path differences like `./foo` and `foo`. */
export declare function sameModule(path1: string, path2: string): boolean;

View file

@ -0,0 +1,377 @@
"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.sameModule = exports.maybeRelativePath = exports.emitImports = exports.SideEffect = exports.Augmented = exports.ImportsAll = exports.ImportsDefault = exports.ImportsName = exports.Imported = exports.Implicit = exports.Import = exports.importType = void 0;
const lodash_1 = __importDefault(require("lodash"));
const path = __importStar(require("path"));
const Node_1 = require("./Node");
const typeImportMarker = '(?:t:)?';
const fileNamePattern = '(?:[a-zA-Z0-9._-]+)';
const modulePattern = `@?(?:(?:${fileNamePattern}(?:/${fileNamePattern})*))`;
const identPattern = `(?:(?:[a-zA-Z][_a-zA-Z0-9.]*)|(?:[_a-zA-Z][_a-zA-Z0-9.]+))`;
exports.importType = '[*@+=]';
const importPattern = `^(${typeImportMarker}${identPattern})?(${exports.importType})(${modulePattern})(?:#(${identPattern}))?`;
const sourceIdentPattern = `(?:(?:${identPattern}:)?)`;
const sourceImportPattern = `^(${typeImportMarker}${sourceIdentPattern}${identPattern})?(@)(${modulePattern})(?:#(${identPattern}))?`;
/**
* Specifies a symbol and its related origin, either via import or implicit/local declaration.
*/
class Import extends Node_1.Node {
constructor(symbol) {
super();
this.symbol = symbol;
}
/**
* Parses a symbol reference pattern to create a symbol. The pattern
* allows the simple definition of all symbol types including any possible
* import variation. If the spec to parse does not follow the proper format
* an implicit symbol is created from the unparsed spec.
*
* Pattern: `symbolName? importType modulePath (#<augmentedSymbolName>)?`
*
* Where:
*
* - `symbolName` is any legal JS/TS symbol. If none, we use the last part of the module path as a guess.
* - `importType` is one of `@` or `*` or `+`, where:
* - `@` is a named import
* - `Foo@bar` becomes `import { Foo } from 'bar'`
* - `*` is a star import,
* - `*Foo` becomes `import * as Foo from 'Foo'`
* - `Foo*foo` becomes `import * as Foo from 'foo'`
* - `+` is an implicit import
* - E.g. `Foo+foo` becomes `import 'foo'`
* - `modulePath` is a path
* - E.g. `<filename>(/<filename)*`
* - augmentedSymbolName = `[a-zA-Z0-9_]+`
*
* Any valid symbol name that represents the symbol that is being augmented. For example,
* the import `rxjs/add/observable/from` attaches the `from` method to the `Observable` class.
* To import it correctly the spec should be `+rxjs/add/observable/from#Observable`. Adding this
* parameter to augmented imports ensures they are output only when the symbol being augmented
* is actually used.
*
*
* @param spec Symbol spec to parse.
* @return Parsed symbol specification
*/
static from(spec) {
let matched = spec.match(importPattern);
if (matched === null) {
matched = spec.match(sourceImportPattern);
}
if (matched != null) {
const modulePath = matched[3];
const kind = matched[2] || '@';
const symbolName = matched[1] || lodash_1.default.last(modulePath.split('/')) || '';
const targetName = matched[4];
switch (kind) {
case '*':
return Import.importsAll(symbolName, modulePath);
case '@':
const isTypeImport = symbolName.startsWith('t:');
let exportedNames;
if (isTypeImport) {
exportedNames = symbolName.substring(2).split(':');
}
else {
exportedNames = symbolName.split(':');
}
const exportedName = exportedNames.pop();
const sourceExportedName = exportedNames[0];
return Import.importsName(exportedName, modulePath, isTypeImport, sourceExportedName);
case '=':
return Import.importsDefault(symbolName, modulePath);
case '+':
return targetName
? Import.augmented(symbolName, modulePath, targetName)
: Import.sideEffect(symbolName, modulePath);
default:
throw new Error('Invalid import kind character');
}
}
return Import.implicit(spec);
}
static fromMaybeString(spec) {
return typeof spec === 'string' ? Import.from(spec) : spec;
}
toCodeString() {
return this.symbol;
}
get childNodes() {
return [];
}
/**
* Creates an import of all the modules exported symbols as a single
* local named symbol
*
* e.g. `import * as Engine from 'templates';`
*
* @param localName The local name of the imported symbols
* @param from The module to import the symbols from
*/
static importsAll(localName, from) {
return new ImportsAll(localName, from);
}
/**
* Creates an import of a single named symbol from the module's exported
* symbols.
*
* e.g. `import { Engine } from 'templates';`
*
* @param exportedName The symbol that is both exported and imported
* @param from The module the symbol is exported from
* @param typeImport whether this is an `import type` import
*/
static importsName(exportedName, from, typeImport, sourceExportedName) {
return new ImportsName(exportedName, from, sourceExportedName, typeImport);
}
/**
* Creates a symbol that is brought in by a whole module import
* that "augments" an existing symbol.
*
* e.g. `import 'rxjs/add/operator/flatMap'`
*
* @param symbolName The augmented symbol to be imported
* @param from The entire import that does the augmentation
* @param target The symbol that is augmented
*/
static augmented(symbolName, from, target) {
return new Augmented(symbolName, from, target);
}
/**
* Creates a symbol that is brought in as a side effect of
* an import.
*
* e.g. `import 'mocha'`
*
* @param symbolName The symbol to be imported
* @param from The entire import that does the augmentation
*/
static sideEffect(symbolName, from) {
return new SideEffect(symbolName, from);
}
/**
* An implied symbol that does no tracking of imports
*
* @param name The implicit symbol name
*/
static implicit(name) {
return new Implicit(name);
}
/**
* Creates an import of a single named symbol from the module's exported
* default.
*
* e.g. `import Engine from 'engine';`
*
* @param exportedName The symbol that is both exported and imported
* @param from The module the symbol is exported from
*/
static importsDefault(exportedName, from) {
return new ImportsDefault(exportedName, from);
}
}
exports.Import = Import;
/**
* Non-imported symbol
*/
class Implicit extends Import {
constructor(symbol) {
super(symbol);
this.source = undefined;
}
}
exports.Implicit = Implicit;
/** Common base class for imported symbols. */
class Imported extends Import {
/** The symbol is the imported symbol, i.e. `BarClass`, and source is the path it comes from. */
constructor(symbol, source) {
super(source);
this.symbol = symbol;
this.source = source;
}
}
exports.Imported = Imported;
/**
* Imports a single named symbol from the module's exported
* symbols.
*
* E.g.:
*
* `import { Engine } from 'templates'` or
* `import { Engine as Engine2 } from 'templates'`
*/
class ImportsName extends Imported {
/**
* @param symbol
* @param source
* @param sourceSymbol is the optional original symbol, i.e if we're renaming the symbol it is `Engine`
* @param typeImport whether this is an `import type` import
*/
constructor(symbol, source, sourceSymbol, typeImport) {
super(symbol, source);
this.sourceSymbol = sourceSymbol;
this.typeImport = typeImport;
}
toImportPiece() {
return this.sourceSymbol ? `${this.sourceSymbol} as ${this.symbol}` : this.symbol;
}
}
exports.ImportsName = ImportsName;
/**
* Imports a single named symbol from the module's exported
* default.
*
* e.g. `import Engine from 'engine';`
*/
class ImportsDefault extends Imported {
constructor(symbol, source) {
super(symbol, source);
}
}
exports.ImportsDefault = ImportsDefault;
/**
* Imports all of the modules exported symbols as a single
* named symbol
*
* e.g. `import * as Engine from 'templates';`
*/
class ImportsAll extends Imported {
constructor(symbol, source) {
super(symbol, source);
}
}
exports.ImportsAll = ImportsAll;
/**
* A symbol that is brought in by a whole module import
* that "augments" an existing symbol.
*
* e.g. `import 'rxjs/add/operator/flatMap'`
*/
class Augmented extends Imported {
constructor(symbol, source, augmented) {
super(symbol, source);
this.augmented = augmented;
}
}
exports.Augmented = Augmented;
/**
* A symbol that is brought in as a side effect of an import.
*
* E.g. `from("Foo+mocha")` will add `import 'mocha'`
*/
class SideEffect extends Imported {
constructor(symbol, source) {
super(symbol, source);
}
}
exports.SideEffect = SideEffect;
/** Generates the `import ...` lines for the given `imports`. */
function emitImports(imports, ourModulePath, importMappings) {
if (imports.length == 0) {
return '';
}
let result = '';
const augmentImports = lodash_1.default.groupBy(filterInstances(imports, Augmented), (a) => a.augmented);
// Group the imports by source module they're imported from
const importsByModule = lodash_1.default.groupBy(imports.filter((it) => it.source !== undefined &&
// Ignore imports that are in our own file
!(it instanceof ImportsName && it.definedIn && sameModule(it.definedIn, ourModulePath))), (it) => it.source);
// Output each source module as one line
Object.entries(importsByModule).forEach(([modulePath, imports]) => {
// Skip imports from the current module
if (sameModule(ourModulePath, modulePath)) {
return;
}
if (modulePath in importMappings) {
modulePath = importMappings[modulePath];
}
const importPath = maybeRelativePath(ourModulePath, modulePath);
// Output star imports individually
unique(filterInstances(imports, ImportsAll).map((i) => i.symbol)).forEach((symbol) => {
result += `import * as ${symbol} from '${importPath}';\n`;
const augments = augmentImports[symbol];
if (augments) {
augments.forEach((augment) => (result += `import '${augment.source}';\n`));
}
});
// Partition named imported into `import type` vs. regular imports
const allNames = filterInstances(imports, ImportsName);
const names = unique(allNames.filter((i) => !i.typeImport).map((it) => it.toImportPiece()));
const def = unique(filterInstances(imports, ImportsDefault).map((it) => it.symbol));
// Output named imports as a group
if (names.length > 0 || def.length > 0) {
const namesPart = names.length > 0 ? [`{ ${names.join(', ')} }`] : [];
const defPart = def.length > 0 ? [def[0]] : [];
result += `import ${[...defPart, ...namesPart].join(', ')} from '${importPath}';\n`;
[...names, ...def].forEach((name) => {
const augments = augmentImports[name];
if (augments) {
augments.forEach((augment) => (result += `import '${augment.source}';\n`));
}
});
}
const typeImports = unique(allNames
.filter((i) => i.typeImport)
.map((it) => it.toImportPiece())
// If the `import type` is already used as a concrete import, just use that
.filter((p) => !names.includes(p)));
if (typeImports.length > 0) {
result += `import type { ${typeImports.join(', ')} } from '${importPath}';\n`;
}
});
const sideEffectImports = lodash_1.default.groupBy(filterInstances(imports, SideEffect), (a) => a.source);
Object.keys(sideEffectImports).forEach((it) => (result += `import '${it}';\n`));
return result;
}
exports.emitImports = emitImports;
function filterInstances(list, t) {
return list.filter((e) => e instanceof t);
}
function unique(list) {
return [...new Set(list)];
}
function maybeRelativePath(outputPath, importPath) {
if (!importPath.startsWith('./')) {
return importPath;
}
importPath = path.normalize(importPath);
outputPath = path.normalize(outputPath);
const outputPathDir = path.dirname(outputPath);
let relativePath = path.relative(outputPathDir, importPath).split(path.sep).join(path.posix.sep);
if (!relativePath.startsWith('.')) {
// ensure the js compiler recognizes this is a relative path.
relativePath = './' + relativePath;
}
return relativePath;
}
exports.maybeRelativePath = maybeRelativePath;
/** Checks if `path1 === path2` despite minor path differences like `./foo` and `foo`. */
function sameModule(path1, path2) {
// TypeScript: import paths ending in .js and .ts are resolved to the .ts file.
// Check the base paths (without the .js or .ts suffix).
const [basePath1, basePath2] = [path1, path2].map((p) => p.replace(/\.[tj]sx?/, ''));
return basePath1 === basePath2 || path.resolve(basePath1) === path.resolve(basePath2);
}
exports.sameModule = sameModule;

View file

@ -0,0 +1,10 @@
import { Node } from './Node';
/**
* A literal source representation of the provided object
*/
export declare class Literal extends Node {
private readonly tokens;
constructor(object: unknown);
get childNodes(): unknown[];
toCodeString(): string;
}

View file

@ -0,0 +1,63 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Literal = void 0;
const Node_1 = require("./Node");
const ConditionalOutput_1 = require("./ConditionalOutput");
const is_plain_object_1 = require("./is-plain-object");
/**
* A literal source representation of the provided object
*/
class Literal extends Node_1.Node {
constructor(object) {
super();
this.tokens = flatten(object);
}
get childNodes() {
return this.tokens;
}
toCodeString() {
return this.tokens
.map((node) => {
if (typeof node === 'string')
return node;
if (node instanceof Node_1.Node)
return node.toCodeString();
return '';
})
.join(' ');
}
}
exports.Literal = Literal;
function flatten(o) {
if (typeof o === 'undefined') {
return ['undefined'];
}
if (typeof o === 'object' && o != null) {
if (o instanceof Node_1.Node || o instanceof ConditionalOutput_1.MaybeOutput) {
return [o];
}
else if (Array.isArray(o)) {
const nodes = ['['];
for (let i = 0; i < o.length; i++) {
if (i !== 0)
nodes.push(',');
nodes.push(...flatten(o[i]));
}
nodes.push(']');
return nodes;
}
else if (is_plain_object_1.isPlainObject(o)) {
const nodes = ['{'];
const entries = Object.entries(o);
for (let i = 0; i < entries.length; i++) {
if (i !== 0)
nodes.push(',');
const [key, value] = entries[i];
nodes.push(JSON.stringify(key), ':', ...flatten(value));
}
nodes.push('}');
return nodes;
}
}
return [JSON.stringify(o)];
}

View file

@ -0,0 +1,6 @@
export declare abstract class Node {
/** Return the unformatted code for this node. */
abstract toCodeString(): string;
/** Any potentially string/SymbolSpec/Code nested nodes within us. */
abstract get childNodes(): unknown[];
}

View file

@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Node = void 0;
class Node {
}
exports.Node = Node;

View file

@ -0,0 +1,190 @@
import { Node } from './Node';
export declare const importType = "[*@+=]";
/**
* Specifies a symbol and its related origin, either via import or implicit/local declaration.
*
* @param value Value of the symbol
*/
export declare abstract class SymbolSpec extends Node {
value: string;
/**
* Parses a symbol reference pattern to create a symbol. The pattern
* allows the simple definition of all symbol types including any possible
* import variation. If the spec to parse does not follow the proper format
* an implicit symbol is created from the unparsed spec.
*
* Pattern: `symbolName? importType modulePath (#<augmentedSymbolName>)?`
*
* Where:
*
* - `symbolName` is any legal JS/TS symbol. If none, we use the last part of the module path as a guess.
* - `importType` is one of `@` or `*` or `+`, where:
* - `@` is a named import
* - `Foo@bar` becomes `import { Foo } from 'bar'`
* - `*` is a star import,
* - `*Foo` becomes `import * as Foo from 'Foo'`
* - `Foo*foo` becomes `import * as Foo from 'foo'`
* - `+` is an implicit import
* - E.g. `Foo+foo` becomes `import 'foo'`
* - `modulePath` is a path
* - E.g. `<filename>(/<filename)*`
* - augmentedSymbolName = `[a-zA-Z0-9_]+`
*
* Any valid symbol name that represents the symbol that is being augmented. For example,
* the import `rxjs/add/observable/from` attaches the `from` method to the `Observable` class.
* To import it correctly the spec should be `+rxjs/add/observable/from#Observable`. Adding this
* parameter to augmented imports ensures they are output only when the symbol being augmented
* is actually used.
*
*
* @param spec Symbol spec to parse.
* @return Parsed symbol specification
*/
static from(spec: string): SymbolSpec;
static fromMaybeString(spec: string | SymbolSpec): SymbolSpec;
/**
* Defined if the symbol is typically imported from a barrel/etc. path, but is technically defined in another file.
*
* We need to know this in case the "this comes from the barrel" type ends up being used in the file where
* the symbol itself is defined, i.e. we don't need an import in that case.
*/
definedIn?: string;
protected constructor(value: string);
toCodeString(): string;
get childNodes(): unknown[];
abstract source: string | undefined;
}
export declare class SymbolSpecs {
/**
* Creates an import of all the modules exported symbols as a single
* local named symbol
*
* e.g. `import * as Engine from 'templates';`
*
* @param localName The local name of the imported symbols
* @param from The module to import the symbols from
*/
static importsAll(localName: string, from: string): SymbolSpec;
/**
* Creates an import of a single named symbol from the module's exported
* symbols.
*
* e.g. `import { Engine } from 'templates';`
*
* @param exportedName The symbol that is both exported and imported
* @param from The module the symbol is exported from
*/
static importsName(exportedName: string, from: string): SymbolSpec;
/**
* Creates a symbol that is brought in by a whole module import
* that "augments" an existing symbol.
*
* e.g. `import 'rxjs/add/operator/flatMap'`
*
* @param symbolName The augmented symbol to be imported
* @param from The entire import that does the augmentation
* @param target The symbol that is augmented
*/
static augmented(symbolName: string, from: string, target: string): SymbolSpec;
/**
* Creates a symbol that is brought in as a side effect of
* an import.
*
* e.g. `import 'mocha'`
*
* @param symbolName The symbol to be imported
* @param from The entire import that does the augmentation
*/
static sideEffect(symbolName: string, from: string): SymbolSpec;
/**
* An implied symbol that does no tracking of imports
*
* @param name The implicit symbol name
*/
static implicit(name: string): SymbolSpec;
/**
* Creates an import of a single named symbol from the module's exported
* default.
*
* e.g. `import Engine from 'engine';`
*
* @param exportedName The symbol that is both exported and imported
* @param from The module the symbol is exported from
*/
static importsDefault(exportedName: string, from: string): SymbolSpec;
}
/**
* Non-imported symbol
*/
export declare class Implicit extends SymbolSpec {
constructor(value: string);
source: undefined;
}
/** Common base class for imported symbols. */
export declare abstract class Imported extends SymbolSpec {
value: string;
source: string;
/** The value is the imported symbol, i.e. `BarClass`, and source is the path it comes from. */
protected constructor(value: string, source: string);
}
/**
* Imports a single named symbol from the module's exported
* symbols.
*
* E.g.:
*
* `import { Engine } from 'templates'` or
* `import { Engine as Engine2 } from 'templates'`
*/
export declare class ImportsName extends Imported {
sourceValue?: string | undefined;
/**
* @param value
* @param source
* @param sourceValue is the optional original value, i.e if we're renaming the symbol it is `Engine`
*/
constructor(value: string, source: string, sourceValue?: string | undefined);
toImportPiece(): string;
}
/**
* Imports a single named symbol from the module's exported
* default.
*
* e.g. `import Engine from 'engine';`
*/
export declare class ImportsDefault extends Imported {
constructor(value: string, source: string);
}
/**
* Imports all of the modules exported symbols as a single
* named symbol
*
* e.g. `import * as Engine from 'templates';`
*/
export declare class ImportsAll extends Imported {
constructor(value: string, source: string);
}
/**
* A symbol that is brought in by a whole module import
* that "augments" an existing symbol.
*
* e.g. `import 'rxjs/add/operator/flatMap'`
*/
export declare class Augmented extends Imported {
augmented: string;
constructor(value: string, source: string, augmented: string);
}
/**
* A symbol that is brought in as a side effect of an
* import.
*
* E.g. `from("Foo+mocha")` will add `import 'mocha'`
*/
export declare class SideEffect extends Imported {
constructor(value: string, source: string);
}
/** Generates the `import ...` lines for the given `imports`. */
export declare function emitImports(imports: SymbolSpec[], ourModulePath: string): string;
export declare function maybeRelativePath(outputPath: string, importPath: string): string;
/** Checks if `path1 === path2` despite minor path differences like `./foo` and `foo`. */
export declare function sameModule(path1: string, path2: string): boolean;

View file

@ -0,0 +1,330 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = __importDefault(require("lodash"));
const path_1 = __importDefault(require("path"));
const Node_1 = require("./Node");
const fileNamePattern = '(?:[a-zA-Z0-9._-]+)';
const modulePattern = `@?(?:(?:${fileNamePattern}(?:/${fileNamePattern})*))`;
const identPattern = `(?:(?:[a-zA-Z][_a-zA-Z0-9.]*)|(?:[_a-zA-Z][_a-zA-Z0-9.]+))`;
exports.importType = '[*@+=]';
const importPattern = `^(${identPattern})?(${exports.importType})(${modulePattern})(?:#(${identPattern}))?`;
/**
* Specifies a symbol and its related origin, either via import or implicit/local declaration.
*
* @param value Value of the symbol
*/
class SymbolSpec extends Node_1.Node {
constructor(value) {
super();
this.value = value;
}
/**
* Parses a symbol reference pattern to create a symbol. The pattern
* allows the simple definition of all symbol types including any possible
* import variation. If the spec to parse does not follow the proper format
* an implicit symbol is created from the unparsed spec.
*
* Pattern: `symbolName? importType modulePath (#<augmentedSymbolName>)?`
*
* Where:
*
* - `symbolName` is any legal JS/TS symbol. If none, we use the last part of the module path as a guess.
* - `importType` is one of `@` or `*` or `+`, where:
* - `@` is a named import
* - `Foo@bar` becomes `import { Foo } from 'bar'`
* - `*` is a star import,
* - `*Foo` becomes `import * as Foo from 'Foo'`
* - `Foo*foo` becomes `import * as Foo from 'foo'`
* - `+` is an implicit import
* - E.g. `Foo+foo` becomes `import 'foo'`
* - `modulePath` is a path
* - E.g. `<filename>(/<filename)*`
* - augmentedSymbolName = `[a-zA-Z0-9_]+`
*
* Any valid symbol name that represents the symbol that is being augmented. For example,
* the import `rxjs/add/observable/from` attaches the `from` method to the `Observable` class.
* To import it correctly the spec should be `+rxjs/add/observable/from#Observable`. Adding this
* parameter to augmented imports ensures they are output only when the symbol being augmented
* is actually used.
*
*
* @param spec Symbol spec to parse.
* @return Parsed symbol specification
*/
static from(spec) {
const matched = spec.match(importPattern);
if (matched != null) {
const modulePath = matched[3];
const type = matched[2] || '@';
const symbolName = matched[1] || lodash_1.default.last(modulePath.split('/')) || '';
const targetName = matched[4];
switch (type) {
case '*':
return SymbolSpecs.importsAll(symbolName, modulePath);
case '@':
return SymbolSpecs.importsName(symbolName, modulePath);
case '=':
return SymbolSpecs.importsDefault(symbolName, modulePath);
case '+':
return targetName
? SymbolSpecs.augmented(symbolName, modulePath, targetName)
: SymbolSpecs.sideEffect(symbolName, modulePath);
default:
throw new Error('Invalid type character');
}
}
return SymbolSpecs.implicit(spec);
}
static fromMaybeString(spec) {
return typeof spec === 'string' ? SymbolSpec.from(spec) : spec;
}
toCodeString() {
return this.value;
}
get childNodes() {
return [];
}
}
exports.SymbolSpec = SymbolSpec;
class SymbolSpecs {
/**
* Creates an import of all the modules exported symbols as a single
* local named symbol
*
* e.g. `import * as Engine from 'templates';`
*
* @param localName The local name of the imported symbols
* @param from The module to import the symbols from
*/
static importsAll(localName, from) {
return new ImportsAll(localName, from);
}
/**
* Creates an import of a single named symbol from the module's exported
* symbols.
*
* e.g. `import { Engine } from 'templates';`
*
* @param exportedName The symbol that is both exported and imported
* @param from The module the symbol is exported from
*/
static importsName(exportedName, from) {
return new ImportsName(exportedName, from);
}
/**
* Creates a symbol that is brought in by a whole module import
* that "augments" an existing symbol.
*
* e.g. `import 'rxjs/add/operator/flatMap'`
*
* @param symbolName The augmented symbol to be imported
* @param from The entire import that does the augmentation
* @param target The symbol that is augmented
*/
static augmented(symbolName, from, target) {
return new Augmented(symbolName, from, target);
}
/**
* Creates a symbol that is brought in as a side effect of
* an import.
*
* e.g. `import 'mocha'`
*
* @param symbolName The symbol to be imported
* @param from The entire import that does the augmentation
*/
static sideEffect(symbolName, from) {
return new SideEffect(symbolName, from);
}
/**
* An implied symbol that does no tracking of imports
*
* @param name The implicit symbol name
*/
static implicit(name) {
return new Implicit(name);
}
/**
* Creates an import of a single named symbol from the module's exported
* default.
*
* e.g. `import Engine from 'engine';`
*
* @param exportedName The symbol that is both exported and imported
* @param from The module the symbol is exported from
*/
static importsDefault(exportedName, from) {
return new ImportsDefault(exportedName, from);
}
}
exports.SymbolSpecs = SymbolSpecs;
/**
* Non-imported symbol
*/
class Implicit extends SymbolSpec {
constructor(value) {
super(value);
this.source = undefined;
}
}
exports.Implicit = Implicit;
/** Common base class for imported symbols. */
class Imported extends SymbolSpec {
/** The value is the imported symbol, i.e. `BarClass`, and source is the path it comes from. */
constructor(value, source) {
super(source);
this.value = value;
this.source = source;
}
}
exports.Imported = Imported;
/**
* Imports a single named symbol from the module's exported
* symbols.
*
* E.g.:
*
* `import { Engine } from 'templates'` or
* `import { Engine as Engine2 } from 'templates'`
*/
class ImportsName extends Imported {
/**
* @param value
* @param source
* @param sourceValue is the optional original value, i.e if we're renaming the symbol it is `Engine`
*/
constructor(value, source, sourceValue) {
super(value, source);
this.sourceValue = sourceValue;
}
toImportPiece() {
return this.sourceValue ? `${this.sourceValue} as ${this.value}` : this.value;
}
}
exports.ImportsName = ImportsName;
/**
* Imports a single named symbol from the module's exported
* default.
*
* e.g. `import Engine from 'engine';`
*/
class ImportsDefault extends Imported {
constructor(value, source) {
super(value, source);
}
}
exports.ImportsDefault = ImportsDefault;
/**
* Imports all of the modules exported symbols as a single
* named symbol
*
* e.g. `import * as Engine from 'templates';`
*/
class ImportsAll extends Imported {
constructor(value, source) {
super(value, source);
}
}
exports.ImportsAll = ImportsAll;
/**
* A symbol that is brought in by a whole module import
* that "augments" an existing symbol.
*
* e.g. `import 'rxjs/add/operator/flatMap'`
*/
class Augmented extends Imported {
constructor(value, source, augmented) {
super(value, source);
this.augmented = augmented;
}
}
exports.Augmented = Augmented;
/**
* A symbol that is brought in as a side effect of an
* import.
*
* E.g. `from("Foo+mocha")` will add `import 'mocha'`
*/
class SideEffect extends Imported {
constructor(value, source) {
super(value, source);
}
}
exports.SideEffect = SideEffect;
/** Generates the `import ...` lines for the given `imports`. */
function emitImports(imports, ourModulePath) {
if (imports.length == 0) {
return '';
}
let result = '';
const augmentImports = lodash_1.default.groupBy(filterInstances(imports, Augmented), (a) => a.augmented);
// Group the imports by source module they're imported from
const importsByModule = lodash_1.default.groupBy(imports.filter((it) => it.source !== undefined &&
!(it instanceof ImportsName && it.definedIn && sameModule(it.definedIn, ourModulePath))), (it) => it.source);
// Output each source module as one line
Object.entries(importsByModule).forEach(([modulePath, imports]) => {
// Skip imports from the current module
if (sameModule(ourModulePath, modulePath)) {
return;
}
const importPath = maybeRelativePath(ourModulePath, modulePath);
// Output star imports individually
filterInstances(imports, ImportsAll).forEach((i) => {
result += `import * as ${i.value} from '${importPath}';\n`;
const augments = augmentImports[i.value];
if (augments) {
augments.forEach((augment) => (result += `import '${augment.source}';\n`));
}
});
// Output named imports as a group
const names = unique(filterInstances(imports, ImportsName).map((it) => it.toImportPiece()));
const def = unique(filterInstances(imports, ImportsDefault).map((it) => it.value));
if (names.length > 0 || def.length > 0) {
const namesPart = names.length > 0 ? [`{ ${names.join(', ')} }`] : [];
const defPart = def.length > 0 ? [def[0]] : [];
const allNames = [...defPart, ...namesPart];
result += `import ${allNames.join(', ')} from '${importPath}';\n`;
[...names, ...def].forEach((name) => {
const augments = augmentImports[name];
if (augments) {
augments.forEach((augment) => (result += `import '${augment.source}';\n`));
}
});
}
});
const sideEffectImports = lodash_1.default.groupBy(filterInstances(imports, SideEffect), (a) => a.source);
Object.keys(sideEffectImports).forEach((it) => (result += `import '${it}';\n`));
return result;
}
exports.emitImports = emitImports;
function filterInstances(list, t) {
return list.filter((e) => e instanceof t);
}
function unique(list) {
return [...new Set(list)];
}
function maybeRelativePath(outputPath, importPath) {
if (!importPath.startsWith('./')) {
return importPath;
}
// Drop the `./` prefix from the outputPath if it exists.
const basePath = outputPath.replace(/^.\//, '');
// Ideally we'd use a path library to do all this.
const numberOfDirs = basePath.split('').filter((l) => l === '/').length;
if (numberOfDirs === 0) {
return importPath;
}
// Make an array of `..` to get our importPath to the root directory.
const a = new Array(numberOfDirs);
const prefix = a.fill('..', 0, numberOfDirs).join('/');
return prefix + importPath.substring(1);
}
exports.maybeRelativePath = maybeRelativePath;
/** Checks if `path1 === path2` despite minor path differences like `./foo` and `foo`. */
function sameModule(path1, path2) {
return path1 === path2 || path_1.default.resolve(path1) === path_1.default.resolve(path2);
}
exports.sameModule = sameModule;

View file

@ -0,0 +1,22 @@
import { Import } from './Import';
import { Code, Def } from './Code';
import { Node } from './Node';
import { ConditionalOutput } from './ConditionalOutput';
export { Code } from './Code';
export { Import } from './Import';
/** A template literal to format code and auto-organize imports. */
export declare function code(literals: TemplateStringsArray, ...placeholders: unknown[]): Code;
export declare function literalOf(object: unknown): Node;
export declare function arrayOf(...elements: unknown[]): Node;
export declare function joinCode(chunks: Code[], opts?: {
on?: string;
trim?: boolean;
}): Code;
/** Creates an import that will be auto-imported at the top of the output file. */
export declare function imp(spec: string, opts?: {
definedIn?: string;
}): Import;
/** Defines `symbol` as being locally defined in the file, to avoid import collisions. */
export declare function def(symbol: string): Def;
/** Creates a conditionally-output code snippet. */
export declare function conditionalOutput(usageSite: string, declarationSite: Code): ConditionalOutput;

View file

@ -0,0 +1,64 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.conditionalOutput = exports.def = exports.imp = exports.joinCode = exports.arrayOf = exports.literalOf = exports.code = exports.Import = exports.Code = void 0;
const Import_1 = require("./Import");
const Code_1 = require("./Code");
const ConditionalOutput_1 = require("./ConditionalOutput");
const is_plain_object_1 = require("./is-plain-object");
const Literal_1 = require("./Literal");
var Code_2 = require("./Code");
Object.defineProperty(exports, "Code", { enumerable: true, get: function () { return Code_2.Code; } });
var Import_2 = require("./Import");
Object.defineProperty(exports, "Import", { enumerable: true, get: function () { return Import_2.Import; } });
/** A template literal to format code and auto-organize imports. */
function code(literals, ...placeholders) {
return new Code_1.Code(literals, placeholders.map((p) => {
if (is_plain_object_1.isPlainObject(p)) {
return literalOf(p);
}
else {
return p;
}
}));
}
exports.code = code;
function literalOf(object) {
return new Literal_1.Literal(object);
}
exports.literalOf = literalOf;
function arrayOf(...elements) {
return literalOf(elements);
}
exports.arrayOf = arrayOf;
function joinCode(chunks, opts = {}) {
const { on = '', trim = true } = opts;
const literals = [''];
for (let i = 0; i < chunks.length - 1; i++) {
literals.push(on);
}
literals.push('');
if (trim) {
chunks.forEach((c) => (c.trim = true));
}
return new Code_1.Code(literals, chunks);
}
exports.joinCode = joinCode;
/** Creates an import that will be auto-imported at the top of the output file. */
function imp(spec, opts = {}) {
const sym = Import_1.Import.from(spec);
if (opts && opts.definedIn) {
sym.definedIn = opts.definedIn;
}
return sym;
}
exports.imp = imp;
/** Defines `symbol` as being locally defined in the file, to avoid import collisions. */
function def(symbol) {
return new Code_1.Def(symbol);
}
exports.def = def;
/** Creates a conditionally-output code snippet. */
function conditionalOutput(usageSite, declarationSite) {
return new ConditionalOutput_1.ConditionalOutput(usageSite, declarationSite);
}
exports.conditionalOutput = conditionalOutput;

View file

@ -0,0 +1,7 @@
/*!
* is-plain-object <https://github.com/jonschlinkert/is-plain-object>
*
* Copyright (c) 2014-2017, Jon Schlinkert.
* Released under the MIT License.
*/
export declare function isPlainObject(o: unknown): boolean;

View file

@ -0,0 +1,31 @@
"use strict";
/*!
* is-plain-object <https://github.com/jonschlinkert/is-plain-object>
*
* Copyright (c) 2014-2017, Jon Schlinkert.
* Released under the MIT License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.isPlainObject = void 0;
function isPlainObject(o) {
if (o === null || o === undefined)
return false;
if (!isObject(o))
return false;
// If has modified constructor
const ctor = o.constructor;
if (ctor === undefined)
return true;
// If has modified prototype
if (!isObject(ctor.prototype))
return false;
// If constructor does not have an Object-specific method
if (!ctor.prototype.hasOwnProperty('isPrototypeOf'))
return false;
// Most likely a plain Object
return true;
}
exports.isPlainObject = isPlainObject;
function isObject(o) {
return Object.prototype.toString.call(o) === '[object Object]';
}