diff --git a/.gitignore b/.gitignore index 4faee3dabf..57e7743df6 100644 --- a/.gitignore +++ b/.gitignore @@ -226,4 +226,5 @@ Samples/**/*.map **/package-lock.json src/*/dist src/*/nm -/nm/ \ No newline at end of file +/nm/ +*.tgz \ No newline at end of file diff --git a/.gulp/typescript.iced b/.gulp/typescript.iced index c777294232..ed747ff03d 100644 --- a/.gulp/typescript.iced +++ b/.gulp/typescript.iced @@ -8,6 +8,9 @@ copyDtsFiles = (done) => # copy *.d.ts files source ["#{basefolder}/src/autorest-core/dist/**/*.d.ts","!#{basefolder}/src/autorest-core/dist/test/**" ] .pipe destination "#{basefolder}/src/autorest/lib/core" + .on 'end', () => + source ["#{basefolder}/src/autorest-core/dist/**/*.d.ts","!#{basefolder}/src/autorest-core/dist/test/**" ] + .pipe destination "#{basefolder}/src/autorest/dist/lib/core" .on 'end', done return null diff --git a/src/autorest-core/lib/autorest-core.ts b/src/autorest-core/lib/autorest-core.ts index 38a0bf46a5..34880c8d5c 100644 --- a/src/autorest-core/lib/autorest-core.ts +++ b/src/autorest-core/lib/autorest-core.ts @@ -41,17 +41,18 @@ export class AutoRest extends EventEmitter { } public static async LiterateToJson(content: string): Promise { + try { let autorest = new AutoRest({ EnumerateFileUris: async function (folderUri: string): Promise> { return []; }, - ReadFile: async (f: string): Promise => f == "mem:///foo.md" ? content : "" + ReadFile: async (f: string): Promise => f == "none:///empty-file.md" ? content || "# empty file" : "# empty file" }); let result = ""; - autorest.AddConfiguration({ "input-file": "mem:///foo.md", "output-artifact": ["swagger-document"] }); + autorest.AddConfiguration({ "input-file": "none:///empty-file.md", "output-artifact": ["swagger-document"] }); autorest.GeneratedFile.Subscribe((source, artifact) => { result = artifact.content; }); // run autorest and wait. - try { + await (await autorest.Process()).finish; return result; } catch (x) { @@ -91,7 +92,6 @@ export class AutoRest extends EventEmitter { return Configuration.DetectConfigurationFile(fileSystem, (documentPath || null)); } - /** * Event: Signals when a Process() finishes. */ @@ -116,6 +116,7 @@ export class AutoRest extends EventEmitter { } /** + * @internal * @param fileSystem The implementation of the filesystem to load and save files from the host application. * @param configFileOrFolderUri The URI of the configuration file or folder containing the configuration file. Is null if no configuration file should be looked for. */ @@ -125,6 +126,9 @@ export class AutoRest extends EventEmitter { process.env["autorest.home"] = process.env["autorest.home"] || homedir(); } + public static create(fileSystem?: IFileSystem, configFileOrFolderUri?: string) { + return new AutoRest(fileSystem, configFileOrFolderUri); + } public async RegenerateView(includeDefault: boolean = false): Promise { this.Invalidate(); const messageEmitter = new MessageEmitter(); diff --git a/src/autorest-core/lib/configuration.ts b/src/autorest-core/lib/configuration.ts index 51bc1817e7..d2621ea8d1 100644 --- a/src/autorest-core/lib/configuration.ts +++ b/src/autorest-core/lib/configuration.ts @@ -463,6 +463,30 @@ export class ConfigurationView { break; } + // fix the source names + for (const source of mx.Source || []) { + + if (source.Position) { + try { + source.document = this.DataStore.ReadStrictSync(source.document).Description; + } catch (e) { + + } + } + } + + // fix the source names in Ranges too + for (const range of mx.Range || []) { + if (range.document) { + try { + range.document = this.DataStore.ReadStrictSync(range.document).Description; + } catch (e) { + + } + } + } + + this.messageEmitter.Message.Dispatch(mx); } } catch (e) { diff --git a/src/autorest-core/main.ts b/src/autorest-core/main.ts index a4f8b11edf..302ece833e 100644 --- a/src/autorest-core/main.ts +++ b/src/autorest-core/main.ts @@ -1,9 +1,13 @@ -#!/usr/bin/env node /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +// enable static modules for autorest-core +if ((global).StaticVolumeSet) { + (global).StaticVolumeSet.addFileSystem(`${__dirname}/static_modules.fs`) +} + export { IFileSystem } from "./lib/file-system" export { Message, Channel } from "./lib/message" export { AutoRest, ConfigurationView } from "./lib/autorest-core" diff --git a/src/autorest-core/package.json b/src/autorest-core/package.json index 50b2678665..2767024370 100644 --- a/src/autorest-core/package.json +++ b/src/autorest-core/package.json @@ -1,6 +1,6 @@ { "name": "@microsoft.azure/autorest-core", - "version": "2.0.4", + "version": "2.0.5", "description": "AutoRest core module", "engines": { "node": ">=7.10.0" diff --git a/src/autorest/app.ts b/src/autorest/app.ts index ffb23632d8..ebeed46394 100644 --- a/src/autorest/app.ts +++ b/src/autorest/app.ts @@ -4,24 +4,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as asyncIO from "@microsoft.azure/async-io"; +import { isFile } from "@microsoft.azure/async-io"; import { cli, enhanceConsole } from "@microsoft.azure/console"; -import { Extension, ExtensionManager } from "@microsoft.azure/extension"; import { Exception, LazyPromise } from "@microsoft.azure/polyfill"; -import * as dns from "dns"; -import { homedir } from "os"; -import { dirname, join, resolve } from "path"; import { Enumerable as IEnumerable, From } from "./lib/ref/linq"; +import { networkEnabled, rootFolder, extensionManager, availableVersions, corePackage, installedCores, tryRequire, resolvePathForLocalVersion, ensureAutorestHome, selectVersion, pkgVersion } from "./autorest-as-a-service" +import { gt } from "semver"; -import * as semver from "semver"; - -// DANGER!!! THIS SWALLOWS BACKSLASHES!!! +// Caution: This may swallow backslashes. // This cost me ~1h of debugging why "console.log(join(homedir(), ".autorest"));" prints "C:\Users\jobader.autorest"... // Or rather left me looking in the wrong place for a file not found error on "C:\Users\jobader.autorest\x\y\z" where the problem was really in "z" enhanceConsole(); -const pkgVersion: string = require(`${__dirname}/../package.json`).version; - // heavy customization, restart from scratch cli.reset(); @@ -87,38 +81,19 @@ const args = cli }) .argv; +// argument tweakin' const preview: boolean = args.preview; -const home: string = process.env["autorest.home"] || homedir(); -process.env["autorest.home"] = home; -console.trace(`.autorest folder location: ${process.env["autorest.home"]}`); -const rootFolder: string = join(home, ".autorest"); -const dotnetFolder: string = join(home, ".dotnet"); - -const basePkgVersion = pkgVersion.indexOf("-") > -1 ? pkgVersion.substring(0, pkgVersion.indexOf("-")) : pkgVersion; - -const corePackage = "@microsoft.azure/autorest-core"; // autorest-core" -const versionRange = `^${basePkgVersion}`; // the version range of the core package required. -const extensionManager: Promise = ExtensionManager.Create(rootFolder); - -// show --info if they use naked --version. -args.info = (args.version === "") || args.info; - +args.info = (args.version === "") || args.info; // show --info if they use unparameterized --version. let requestedVersion: string = args.version || (args.latest && "latest") || (args.preview && "preview") || "latest-installed"; const listAvailable: boolean = args.autorest["list-available"] || false; let force = args.force || false; -// get network status -const networkEnabled: Promise = new Promise((r, j) => { - dns.lookup("8.8.8.8", 4, (err, address, family) => { - r(err ? false : true); - }); -}); - +/** Check if there is an update for the bootstrapper available. */ const checkBootstrapper = new LazyPromise(async () => { if (await networkEnabled) { try { const pkg = await (await extensionManager).findPackage("autorest", preview ? "preview" : "latest"); - if (semver.gt(pkg.version, pkgVersion)) { + if (gt(pkg.version, pkgVersion)) { console.log(`\n ## There is a new version of AutoRest available (${pkg.version}).\n > You can install the newer version with with \`npm install -g autorest@${preview ? "preview" : "latest"}\`\n`); } } catch (e) { @@ -127,38 +102,13 @@ const checkBootstrapper = new LazyPromise(async () => { } }); -async function availableVersions() { - if (await networkEnabled) { - try { - const vers = (await (await extensionManager).getPackageVersions(corePackage)).sort((b, a) => semver.compare(a, b)); - const result = new Array(); - for (const ver of vers) { - if (semver.satisfies(ver, versionRange)) { - result.push(ver); - } - } - return result; - } catch (e) { - console.trace(`No available versions of package ${corePackage} found.`); - } - - } else { - console.trace(`Skipping getting available versions because network is not detected.`); - } - return []; -}; - -// result = extensions.filter(ext => ext.name === corePackage); - +/** Shows the valid available autorest core packages. */ async function showAvailableCores(): Promise { let table = ""; let max = 10; for (const v of await availableVersions()) { max--; - - if (preview || !semver.prerelease(v)) { - table += `\n|${corePackage}|${v}|`; - } + table += `\n|${corePackage}|${v}|`; if (!max) { break; } @@ -170,6 +120,7 @@ async function showAvailableCores(): Promise { return 0; } +/** Shows all the autorest extensions that are installed. */ async function showInstalledExtensions(): Promise { const extensions = await (await extensionManager).getInstalledExtensions(); let table = ""; @@ -186,49 +137,35 @@ async function showInstalledExtensions(): Promise { return 0; } -async function installedCores() { - const extensions = await (await extensionManager).getInstalledExtensions(); - const result = (extensions.length > 0) ? extensions.filter(ext => ext.name === corePackage && semver.satisfies(ext.version, versionRange)) : new Array(); - return result.sort((a, b) => semver.compare(b.version, a.version)); -}; - -function IsUri(uri: string): boolean { - return /^([a-z0-9+.-]+):(?:\/\/(?:((?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*)@)?((?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*)(?::(\d*))?(\/(?:[a-z0-9-._~!$&'()*+,;=:@/]|%[0-9A-F]{2})*)?|(\/?(?:[a-z0-9-._~!$&'()*+,;=:@]|%[0-9A-F]{2})+(?:[a-z0-9-._~!$&'()*+,;=:@/]|%[0-9A-F]{2})*)?)(?:\?((?:[a-z0-9-._~!$&'()*+,;=:/?@]|%[0-9A-F]{2})*))?(?:#((?:[a-z0-9-._~!$&'()*+,;=:/?@]|%[0-9A-F]{2})*))?$/i.test(uri); -} - +/** Main Entrypoint for AutoRest Bootstrapper */ async function main() { if (args.help) { + // yargs will print the help. We can leave now. process.exit(0); } - try { - // check to see if local installed core is available. - const localVersion = args.version ? resolve(requestedVersion) : dirname(require.resolve("@microsoft.azure/autorest-core/package.json")); + // check to see if local installed core is available. + const localVersion = resolvePathForLocalVersion(args.version && args.version !== '' ? requestedVersion : null); - // did they specify the package directory directly - if (await asyncIO.isDirectory(localVersion)) { - if (require(`${localVersion}/package.json`).name === corePackage) { - console.trace(`Using local core from: '${localVersion}'`); - require(`${localVersion}/dist/app.js`); - return; - } - } - - if (await asyncIO.isFile(localVersion)) { - // this should try to install the file. - console.trace(`Found local core package file: '${localVersion}'`); - requestedVersion = localVersion; - } - } catch (e) { + // try to use a specified folder or one in node_modules if it is there. + if (await tryRequire(localVersion, "app.js")) { + return; + } - } // failing that, we'll continue on and see if NPM can do something with the version. + // if the resolved local version is actually a file, we'll try that as a package when we get there. + if (await isFile(localVersion)) { + // this should try to install the file. + console.trace(`Found local core package file: '${localVersion}'`); + requestedVersion = localVersion; + } + // failing that, we'll continue on and see if NPM can do something with the version. console.trace(`Network Enabled: ${await networkEnabled}`); try { - await asyncIO.mkdir(rootFolder); - await asyncIO.mkdir(dotnetFolder); + /* make sure we have a .autorest folder */ + await ensureAutorestHome(); if (args.reset) { console.trace(`Resetting autorest extension folder '${rootFolder}'`); @@ -248,65 +185,11 @@ async function main() { process.exit(await showInstalledExtensions()); } - const installedVersions = await installedCores(); - const currentVersion = From(installedVersions).FirstOrDefault() || null; - - if (currentVersion) { - console.trace(`The most recent installed version is ${currentVersion.version}`); - - if (requestedVersion === "latest-installed" || (requestedVersion === 'latest' && false == await networkEnabled)) { - requestedVersion = currentVersion.version; - } - } else { - console.trace(`No ${corePackage} is installed.`); - } - - let selectedVersion = From(installedVersions).FirstOrDefault(each => semver.satisfies(each.version, requestedVersion)); - - // is the requested version installed? - if (!selectedVersion || force) { - if (!force) { - console.trace(`${requestedVersion} was not satisfied directly by a previous installation.`); - } - - // if it's not a file, and the network isn't available, we can't continue. - if (!await asyncIO.isFile(requestedVersion) && !(await networkEnabled)) { - // no network enabled. - throw new Exception(`Network access is not available, requested version '${requestedVersion}' is not installed. `); - } - - // after this point, latest-installed must mean latest. - if (requestedVersion === 'latest-installed') { - requestedVersion = 'latest'; - } - - // if they have requested 'latest' -- they really mean latest with same major version number - if (requestedVersion === 'latest') { - requestedVersion = versionRange; - } - - const pkg = await (await extensionManager).findPackage(corePackage, requestedVersion); - if (pkg) { - console.trace(`Selected package: ${pkg.name}@${pkg.version} => ${pkg.resolvedInfo.rawSpec} `); - } else { - throw new Exception(`Unable to find a valid AutoRest Core package for '${requestedVersion}'.`); - } - - // pkg.version == the actual version - // check if it's installed already. - selectedVersion = await (await extensionManager).getInstalledExtension(corePackage, pkg.version); - - if (!selectedVersion || force) { - // this will throw if there is an issue with installing the extension. - console.trace(`**Installing package** ${corePackage}@${pkg.version}\n[This will take a few moments...]`); - - selectedVersion = await (await extensionManager).installPackage(pkg, force, 5 * 60 * 1000, installer => installer.Message.Subscribe((s, m) => { console.trace(`Installer: ${m}`); })); - console.trace(`Extension location: ${selectedVersion.packageJsonPath}`); - } else { - console.trace(`AutoRest Core ${pkg.version} is available at ${selectedVersion.modulePath}`); - } - } + // logic to resolve and optionally install a autorest core package. + // will throw if it's not doable. + let selectedVersion = await selectVersion(requestedVersion, force); + // let's strip the extra stuff from the command line before we require the core module. const RemoveArgs = From(["--version", "--list-installed", "--list-available", "--reset", "--latest", "--latest-release", "--runtime-id"]); // Remove bootstrapper args from cmdline process.argv = From(process.argv).Where(each => !RemoveArgs.Any(i => each === i || each.startsWith(`${i}=`) || each.startsWith(`${i}:`))).ToArray(); @@ -319,7 +202,9 @@ async function main() { } console.trace(`Starting ${corePackage} from ${await selectedVersion.location}`); - require(join(await selectedVersion.modulePath, "dist/app.js")); + if (!tryRequire(await selectedVersion.modulePath, "app.js")) { + throw new Error(`Unable to start AutoRest Core from ${await selectedVersion.modulePath}`); + } } catch (exception) { console.log("Failure:"); console.error(exception); @@ -328,28 +213,3 @@ async function main() { } main(); - - - /* - // maybe they passed a path or uri to the package. - if (await asyncIO.isFile(requestedVersion) || IsUri(requestedVersion)) { - console.trace(`Using package from local or uri path: '${requestedVersion}'`); - try { - const pkg = await (await extensionManager).findPackage(corePackage, requestedVersion); - - selectedVersion = From(await (await extensionManager).getInstalledExtensions()).FirstOrDefault(each => each.name === pkg.name && each.version === pkg.version); - if (selectedVersion) { - console.trace(`Version ${selectedVersion} is currently installed.`); - break; - } - - // try to install and use what they provided. - - console.trace(`Package Info:'${pkg.version}' `); - } catch (e) { - // doesn't appear installed or anything, we'll let it get installed. - } - } - // nope -- let's try to get the version requested - console.trace(`Requested version '${requestedVersion}' is not yet installed.`); -*/ diff --git a/src/autorest/autorest-as-a-service.ts b/src/autorest/autorest-as-a-service.ts new file mode 100644 index 0000000000..e2e64eeb63 --- /dev/null +++ b/src/autorest/autorest-as-a-service.ts @@ -0,0 +1,151 @@ + +import { lookup } from "dns"; +import { Extension, ExtensionManager } from "@microsoft.azure/extension"; +import { homedir } from "os"; +import { dirname, join, resolve } from "path"; +import { Enumerable as IEnumerable, From } from "./lib/ref/linq"; +import { Exception, LazyPromise } from "@microsoft.azure/polyfill"; + +import * as semver from "semver"; +import * as asyncIO from "@microsoft.azure/async-io"; + +export const pkgVersion: string = require(`${__dirname}/../package.json`).version; +const home: string = process.env["autorest.home"] || homedir(); +process.env["autorest.home"] = home; + +export const rootFolder: string = join(home, ".autorest"); + +export const extensionManager: Promise = ExtensionManager.Create(rootFolder); +export const corePackage = "@microsoft.azure/autorest-core"; // autorest-core" +const basePkgVersion = pkgVersion.indexOf("-") > -1 ? pkgVersion.substring(0, pkgVersion.indexOf("-")) : pkgVersion; +const versionRange = `^${basePkgVersion}`; // the version range of the core package required. + +export const networkEnabled: Promise = new Promise((r, j) => { + lookup("8.8.8.8", 4, (err, address, family) => { + r(err ? false : true); + }); +}); + +export async function availableVersions() { + if (await networkEnabled) { + try { + const vers = (await (await extensionManager).getPackageVersions(corePackage)).sort((b, a) => semver.compare(a, b)); + const result = new Array(); + for (const ver of vers) { + if (semver.satisfies(ver, versionRange)) { + result.push(ver); + } + } + return result; + } catch (e) { + console.info(`No available versions of package ${corePackage} found.`); + } + + } else { + console.info(`Skipping getting available versions because network is not detected.`); + } + return []; +}; + + +export async function installedCores() { + const extensions = await (await extensionManager).getInstalledExtensions(); + const result = (extensions.length > 0) ? extensions.filter(ext => ext.name === corePackage && semver.satisfies(ext.version, versionRange)) : new Array(); + return result.sort((a, b) => semver.compare(b.version, a.version)); +}; + +export function resolvePathForLocalVersion(requestedVersion: string | null): string | null { + try { + return requestedVersion ? resolve(requestedVersion) : dirname(require.resolve("@microsoft.azure/autorest-core/package.json")); + } catch (e) { + + } + return null; +} + +export async function tryRequire(localPath: string | null, entrypoint: string): Promise { + try { + // did they specify the package directory directly + if (await asyncIO.isDirectory(localPath)) { + if (require(`${localPath}/package.json`).name === corePackage) { + console.trace(`Using core from: '${localPath}'`); + return require(`${localPath}/dist/${entrypoint}`); + } + } + } catch (e) { + + } + return false; +} + +export async function ensureAutorestHome() { + await asyncIO.mkdir(rootFolder); +} + +export async function selectVersion(requestedVersion: string, force: boolean, minimumVersion?: string) { + const installedVersions = await installedCores(); + let currentVersion = From(installedVersions).FirstOrDefault() || null; + + // the consumer can say I want the latest-installed, but at least XXX.XXX + if (minimumVersion && currentVersion && !semver.satisfies(currentVersion.version, minimumVersion)) { + currentVersion = null; + } + + if (currentVersion) { + console.trace(`The most recent installed version is ${currentVersion.version}`); + + if (requestedVersion === "latest-installed" || (requestedVersion === 'latest' && false == await networkEnabled)) { + console.trace(`requesting current version '${currentVersion.version}'`); + requestedVersion = currentVersion.version; + } + } else { + console.trace(`No ${corePackage} is installed.`); + } + + let selectedVersion = From(installedVersions).FirstOrDefault(each => semver.satisfies(each.version, requestedVersion)); + + // is the requested version installed? + if (!selectedVersion || force) { + if (!force) { + console.trace(`${requestedVersion} was not satisfied directly by a previous installation.`); + } + + // if it's not a file, and the network isn't available, we can't continue. + if (!await asyncIO.isFile(requestedVersion) && !(await networkEnabled)) { + // no network enabled. + throw new Exception(`Network access is not available, requested version '${requestedVersion}' is not installed. `); + } + + // after this point, latest-installed must mean latest. + if (requestedVersion === 'latest-installed') { + requestedVersion = 'latest'; + } + + // if they have requested 'latest' -- they really mean latest with same major version number + if (requestedVersion === 'latest') { + requestedVersion = versionRange; + } + + const pkg = await (await extensionManager).findPackage(corePackage, requestedVersion); + if (pkg) { + console.trace(`Selected package: ${pkg.name}@${pkg.version} => ${pkg.resolvedInfo.rawSpec} `); + } else { + throw new Exception(`Unable to find a valid AutoRest Core package for '${requestedVersion}'.`); + } + + // pkg.version == the actual version + // check if it's installed already. + selectedVersion = await (await extensionManager).getInstalledExtension(corePackage, pkg.version); + + if (!selectedVersion || force) { + // this will throw if there is an issue with installing the extension. + console.trace(`**Installing package** ${corePackage}@${pkg.version}\n[This will take a few moments...]`); + + selectedVersion = await (await extensionManager).installPackage(pkg, force, 5 * 60 * 1000, installer => installer.Message.Subscribe((s, m) => { console.trace(`Installer: ${m}`); })); + console.trace(`Extension location: ${selectedVersion.packageJsonPath}`); + } else { + console.trace(`AutoRest Core ${pkg.version} is available at ${selectedVersion.modulePath}`); + } + } + return selectedVersion; +} \ No newline at end of file diff --git a/src/autorest/main.ts b/src/autorest/main.ts index fb282154cd..a1815e0505 100644 --- a/src/autorest/main.ts +++ b/src/autorest/main.ts @@ -1,30 +1,89 @@ + +// enable the static module loader +import { initialize as initLoader } from "./static-loader"; +initLoader(); + +// everything else. +import { networkEnabled, rootFolder, extensionManager, availableVersions, corePackage, installedCores, tryRequire, resolvePathForLocalVersion, ensureAutorestHome, selectVersion, pkgVersion } from "./autorest-as-a-service" import { DocumentPatterns } from './lib/core/lib/document-type'; +import { resolve } from 'path'; // exports the public AutoRest definitions -export { IEvent } from './lib/core/lib/events'; export { IFileSystem, Message } from './lib/core/main'; // the local class definition of the AutoRest Interface and the EventEmitter signatures -import { AutoRest as IAutoRest, Channel as IChannel } from './lib/core/main'; -import { EventEmitter as IEventEmitter } from './lib/core/lib/events'; +import { AutoRest as IAutoRest, Channel as IChannel, IFileSystem } from './lib/core/main'; + +export enum Channel { + Information = "information", + Warning = "warning", + Error = "error", + Debug = "debug", + Verbose = "verbose", + Fatal = "fatal", +} + +let resolve_autorest: (value?: typeof IAutoRest | PromiseLike) => void; +let reject_autorest: (reason?: any) => void; // export the selected implementation of the AutoRest interface. +export declare type AutoRest = IAutoRest; +export const AutoRest: Promise = new Promise((r, j) => { + resolve_autorest = r; + reject_autorest = j; +}); +let busy = false; +let loaded = false; -function AutorestImplementationPath(): string { - // return join(Installer.AutorestFolder, this.LatestAutorestVersion, 'node_modules', 'autorest-core'); - return "FOOO"; -} +export async function initialize(requestedVersion: string = "latest-installed", minimumVersion?: string) { + if (loaded) { + return; + } + if (busy) { + throw new Error("initialize is already called.") + } -let modulePath = /(\\||\/)src(\\||\/)autorest(\\||\/)/.test(__dirname) ? `${__dirname}/../autorest-core` : AutorestImplementationPath() -let impl = require(modulePath); + busy = true; -export const AutoRest: typeof IAutoRest = impl.AutoRest -export declare type AutoRest = IAutoRest; + try { + await ensureAutorestHome(); + + try { + // did they pass in a path first? + const localVersion = resolve(requestedVersion); + + // try to use a specified folder + const module = await tryRequire(localVersion, "main.js") + if (module) { + // assign the type to the Async Class Identity + resolve_autorest(module.AutoRest) + loaded = true; + return; + } + } catch (E) { + // no local version + } + // logic to resolve and optionally install a autorest core package. + // will throw if it's not doable. + let selectedVersion = await selectVersion(requestedVersion, false, minimumVersion); + + const module = await tryRequire(await selectedVersion.modulePath, "main.js"); + if (!module) { + reject_autorest(new Error(`Unable to start AutoRest Core from ${requestedVersion}/${await selectedVersion.modulePath}`)); + throw new Error(`Unable to start AutoRest Core from ${requestedVersion}/${await selectedVersion.modulePath}`); + } -export const Channel: typeof IChannel = impl.Channel -export declare type Channel = IChannel; + // assign the type to the Async Class Identity + resolve_autorest(module.AutoRest) + loaded = true; -let event_impl = require(modulePath + "/lib/events"); -export const EventEmitter: typeof IEventEmitter = event_impl.EventEmitter -export declare type EventEmitter = IEventEmitter; + } finally { + busy = false; + } +} + +export async function create(fileSystem?: IFileSystem, configFileOrFolderUri?: string): Promise { + const CAutoRest = (await AutoRest); + return new CAutoRest(fileSystem, configFileOrFolderUri); +} diff --git a/src/autorest/package.json b/src/autorest/package.json index c6b8d4a4f7..e3b83a53f2 100644 --- a/src/autorest/package.json +++ b/src/autorest/package.json @@ -1,6 +1,6 @@ { "name": "autorest", - "version": "2.0.2", + "version": "2.0.5", "description": "The AutoRest tool generates client libraries for accessing RESTful web services. Input to AutoRest is a spec that describes the REST API using the Open API Initiative format.", "engines": { "node": ">=7.10.0" @@ -28,7 +28,7 @@ "scripts": { "start": "node ./dist/static-app.js", "test": "./node_modules/.bin/mocha ./dist/test", - "prepare": "shx rm -rf ./dist/ && shx mkdir -p ./dist/ && shx cp -R ./node_modules/fs-monkey/lib/ ./dist/fs-monkey/ && cd ./static_modules && npm install", + "prepare": "shx mkdir -p ./dist/ && shx cp -R ./node_modules/fs-monkey/lib/ ./dist/fs-monkey/ && cd ./static_modules && npm install", "prepublishonly": "gulp build" }, "typings": "./dist/main.d.ts", diff --git a/src/autorest/static-app.ts b/src/autorest/static-app.ts index b6d482c507..08dc62b10e 100644 --- a/src/autorest/static-app.ts +++ b/src/autorest/static-app.ts @@ -1,28 +1,5 @@ #!/usr/bin/env node -import { StaticVolumeSet } from './static-fs' +import { initialize } from "./static-loader"; -// get patchRequire -const patchRequire = require("./fs-monkey/index.js").patchRequire; -const patchFs = require("./fs-monkey/index.js").patchFs; - -// create the static volume set -const staticVolume = new StaticVolumeSet(`${__dirname}/static_modules.fs`, true); - -// patch require -patchRequire(staticVolume); - -// patch the fs too (fixes readFileSync) -patchFs(staticVolume); - -// cheat: add static volume instance to the global namespace so that autorest core can add to it. -(global).StaticVolumeSet = staticVolume; - -// hot-patch process.exit so that when it's called we shutdown the patcher early -const process_exit = process.exit; -process.exit = (n): never => { - staticVolume.shutdown(); - return process_exit(n); -} - -// continue with original startup -require("./app"); \ No newline at end of file +initialize(); +require('./app'); \ No newline at end of file diff --git a/src/autorest/static-loader.ts b/src/autorest/static-loader.ts new file mode 100644 index 0000000000..9f9096ff01 --- /dev/null +++ b/src/autorest/static-loader.ts @@ -0,0 +1,33 @@ +import { StaticVolumeSet } from './static-fs' + +export function initialize(): StaticVolumeSet { + // only do this once, ever. + if ((global).StaticVolumeSet) { + return (global).StaticVolumeSet; + } + + // get patchRequire + const patchRequire = require("./fs-monkey/index.js").patchRequire; + const patchFs = require("./fs-monkey/index.js").patchFs; + + // create the static volume set + const staticVolume = new StaticVolumeSet(`${__dirname}/static_modules.fs`, true); + + // patch require + patchRequire(staticVolume); + + // patch the fs too (fixes readFileSync) + patchFs(staticVolume); + + // cheat: add static volume instance to the global namespace so that autorest core can add to it. + (global).StaticVolumeSet = staticVolume; + + // hot-patch process.exit so that when it's called we shutdown the patcher early + const process_exit = process.exit; + process.exit = (n): never => { + staticVolume.shutdown(); + return process_exit(n); + } + + return staticVolume; +}