import * as path from 'path' ; import importLazy = require ( 'import-lazy' ) ; import * as Resolve from 'resolve' ; import nodeModule = require ( 'module' ) ; import { PackageJsonLookup } from './PackageJsonLookup' ; import { FileSystem } from './FileSystem' ; import { IPackageJson } from './IPackageJson' ; /**
 * Common options shared by  IImportResolveModuleOptions and  IImportResolvePackageOptions public export interface IImportResolveOptions { /**
     * The path from which  IImportResolveModuleOptions . modulePath or
     *  IImportResolvePackageOptions . packageName should be resolved.
      baseFolderPath : string ; remarks includeSystemModules ? : boolean ; remarks allowSelfReference ? : boolean ; } /**
 * Options for  Import . resolveModule public export interface IImportResolveModuleOptions extends IImportResolveOptions { /**
     * The module identifier to resolve. For example "\@rushstack/node-core-library" or
     * "\@rushstack/node-core-library/lib/index.js"
     */ modulePath : string ; } /**
 * Options for  Import . resolvePackage public export interface IImportResolvePackageOptions extends IImportResolveOptions { /**
     * The package name to resolve. For example "\@rushstack/node-core-library"
     */ packageName : string ; } interface IPackageDescriptor { packageRootPath : string ; packageName : string ; } public export class Import { private static __builtInModules : Set < string > | undefined ; private static get _builtInModules ( ) : Set < string > { if ( ! Import . __builtInModules ) { Import . __builtInModules = new Set < string > ( nodeModule . builtinModules ) ; } return Import . __builtInModules ; } remarks This is a more structured wrapper for the  https * package.  It enables you to replace an import like this:
     *
     * ```ts
     * import * as example from 'example'; // <-- 100ms load time
     *
     * if (condition) {
     *   example.doSomething();
     * }
     * ```
     *
     * ...with a pattern like this:
     *
     * ```ts
     * const example: typeof import('example') = Import.lazy('example', require);
     *
     * if (condition) {
     *   example.doSomething(); // <-- 100ms load time occurs here, only if needed
     * }
     * ```
     *
     * The implementation relies on JavaScript's `Proxy` feature to intercept access to object members.  Thus
     * it will only work correctly with certain types of module exports.  If a particular export isn't well behaved,
     * you may need to find (or introduce) some other module in your dependency graph to apply the optimization to.
     *
     * Usage guidelines:
     *
     * - Always specify types using `typeof` as shown above.
     *
     * - Never apply lazy-loading in a way that would convert the module's type to `any`. Losing type safety
     *   seriously impacts the maintainability of the code base.
     *
     * - In cases where the non-runtime types are needed, import them separately using the `Types` suffix:
     *
     * ```ts
     * const example: typeof import('example') = Import.lazy('example', require);
     * import type * as exampleTypes from 'example';
     * ```
     *
     * - If the imported module confusingly has the same name as its export, then use the Module suffix:
     *
     * ```ts
     * const exampleModule: typeof import('../../logic/Example') = Import.lazy(
     *   '../../logic/Example', require);
     * import type * as exampleTypes from '../../logic/Example';
     * ```
     *
     * - If the exports cause a lot of awkwardness (e.g. too many expressions need to have `exampleModule.` inserted
     *   into them), or if some exports cannot be proxied (e.g. `Import.lazy('example', require)` returns a function
     *   signature), then do not lazy-load that module.  Instead, apply lazy-loading to some other module which is
     *   better behaved.
     *
     * - It's recommended to sort imports in a standard ordering:
     *
     * ```ts
     * // 1. external imports
     * import * as path from 'path';
     * import { Import, JsonFile, JsonObject } from '@rushstack/node-core-library';
     *
     * // 2. local imports
     * import { LocalFile } from './path/LocalFile';
     *
     * // 3. lazy-imports (which are technically variables, not imports)
     * const semver: typeof import('semver') = Import.lazy('semver', require);
     * ```
      public static lazy ( moduleName , require ) { const importLazyLocal = importLazy ( require ) ; return importLazyLocal ( moduleName ) ; } remarks A module path is a text string that might appear in a statement such as
     * `import { X } from "____";` or `const x = require("___");`.  The implementation is based
     * on the popular `resolve` NPM package.
     *
     * Suppose `example` is an NPM package whose entry point is `lib/index.js`:
     * ```ts
     * // Returns "/path/to/project/node_modules/example/lib/index.js"
     * Import.resolveModule({ modulePath: 'example' });
     *
     * // Returns "/path/to/project/node_modules/example/lib/other.js"
     * Import.resolveModule({ modulePath: 'example/lib/other' });
     * ```
     * If you need to determine the containing package folder
     * (`/path/to/project/node_modules/example`), use  Import . resolvePackage instead.
     *
     *  returns the absolute path of the resolved module.
     * If  IImportResolveOptions . includeSystemModules is specified
     * and a system module is found, then its name is returned without any file path.
      public static resolveModule ( options ) { const { modulePath } = options ; if ( path . isAbsolute ( modulePath ) ) { return modulePath ; } const normalizedRootPath = FileSystem . getRealPath ( options . baseFolderPath ) ; if ( modulePath . startsWith ( '.' ) ) { return path . resolve ( normalizedRootPath , modulePath ) ; } if ( options . includeSystemModules === true && Import . _builtInModules . has ( modulePath ) ) { return modulePath ; } if ( options . allowSelfReference === true ) { const ownPackage = Import . _getPackageName ( options . baseFolderPath ) ; if ( ownPackage && modulePath . startsWith ( ownPackage . packageName ) ) { const packagePath = modulePath . substr ( ownPackage . packageName . length + 1 ) ; return path . resolve ( ownPackage . packageRootPath , packagePath ) ; } } try { return Resolve . sync ( options . includeSystemModules !== true && modulePath . indexOf ( '/' ) === - 1 ? `${ modulePath }/` : modulePath , { basedir : normalizedRootPath , preserveSymlinks : false } ) ; } catch ( e ) { throw new Error ( `Cannot find module "${ modulePath }" from "${ options . baseFolderPath }".` ) ; } } remarks Suppose `example` is an NPM package whose entry point is `lib/index.js`:
     * ```ts
     * // Returns "/path/to/project/node_modules/example"
     * Import.resolvePackage({ packageName: 'example' });
     * ```
     *
     * If you need to resolve a module path, use  Import . resolveModule instead:
     * ```ts
     * // Returns "/path/to/project/node_modules/example/lib/index.js"
     * Import.resolveModule({ modulePath: 'example' });
     * ```
     *
     *  returns the absolute path of the package folder.
     * If  IImportResolveOptions . includeSystemModules is specified
     * and a system module is found, then its name is returned without any file path.
      public static resolvePackage ( options ) { const { packageName } = options ; if ( options . includeSystemModules && Import . _builtInModules . has ( packageName ) ) { return packageName ; } const normalizedRootPath = FileSystem . getRealPath ( options . baseFolderPath ) ; if ( options . allowSelfReference ) { const ownPackage = Import . _getPackageName ( options . baseFolderPath ) ; if ( ownPackage && ownPackage . packageName === packageName ) { return ownPackage . packageRootPath ; } } try { const resolvedPath = Resolve . sync ( packageName , { basedir : normalizedRootPath , preserveSymlinks : false , packageFilter : ( pkg ) : { main : string ; } => { pkg . main = 'package.json' ; return pkg ; } } ) ; const packagePath = path . dirname ( resolvedPath ) ; const packageJson = PackageJsonLookup . instance . loadPackageJson ( path . join ( packagePath , 'package.json' ) ) ; if ( packageJson . name === packageName ) { return packagePath ; } else { throw new Error ( ) ; } } catch ( e ) { throw new Error ( `Cannot find package "${ packageName }" from "${ options . baseFolderPath }".` ) ; } } private static _getPackageName ( rootPath ) { const packageJsonPath = PackageJsonLookup . instance . tryGetPackageJsonFilePathFor ( rootPath ) ; if ( packageJsonPath ) { const packageJson = PackageJsonLookup . instance . loadPackageJson ( packageJsonPath ) ; return { packageRootPath : path . dirname ( packageJsonPath ) , packageName : packageJson . name } ; } else { return undefined ; } } }