node_modules ignore

This commit is contained in:
oier committed 2025-05-08 23:43:47 +02:00
1 parent e19d52f172
commit 4574544c9f
65041 files changed
+10593536

No files matched your search

@@ -0,0 +1,36 @@
/**
* This exception can be thrown to indicate that an operation failed and an error message has already
* been reported appropriately. Thus, the catch handler does not have responsibility for reporting
* the error.
*
* @remarks
* For example, suppose a tool writes interactive output to `console.log()`. When an exception is thrown,
* the `catch` handler will typically provide simplistic reporting such as this:
*
* ```ts
* catch (error) {
* console.log("ERROR: " + error.message);
* }
* ```
*
* Suppose that the code performing the operation normally prints rich output to the console. It may be able to
* present an error message more nicely (for example, as part of a table, or structured log format). Throwing
* `AlreadyReportedError` provides a way to use exception handling to abort the operation, but instruct the `catch`
* handler not to print an error a second time:
*
* ```ts
* catch (error) {
* if (error instanceof AlreadyReportedError) {
* return;
* }
* console.log("ERROR: " + error.message);
* }
* ```
*
* @public
*/
export declare class AlreadyReportedError extends Error {
constructor();
static [Symbol.hasInstance](instance: object): boolean;
}
//# sourceMappingURL=AlreadyReportedError.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"AlreadyReportedError.d.ts","sourceRoot":"","sources":["../src/AlreadyReportedError.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;;WAW/B,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAG9D"}
@@ -0,0 +1,54 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.AlreadyReportedError = void 0;
const TypeUuid_1 = require("./TypeUuid");
const uuidAlreadyReportedError = 'f26b0640-a49b-49d1-9ead-1a516d5920c7';
/**
* This exception can be thrown to indicate that an operation failed and an error message has already
* been reported appropriately. Thus, the catch handler does not have responsibility for reporting
* the error.
*
* @remarks
* For example, suppose a tool writes interactive output to `console.log()`. When an exception is thrown,
* the `catch` handler will typically provide simplistic reporting such as this:
*
* ```ts
* catch (error) {
* console.log("ERROR: " + error.message);
* }
* ```
*
* Suppose that the code performing the operation normally prints rich output to the console. It may be able to
* present an error message more nicely (for example, as part of a table, or structured log format). Throwing
* `AlreadyReportedError` provides a way to use exception handling to abort the operation, but instruct the `catch`
* handler not to print an error a second time:
*
* ```ts
* catch (error) {
* if (error instanceof AlreadyReportedError) {
* return;
* }
* console.log("ERROR: " + error.message);
* }
* ```
*
* @public
*/
class AlreadyReportedError extends Error {
constructor() {
super('An error occurred.');
// Manually set the prototype, as we can no longer extend built-in classes like Error, Array, Map, etc
// [https://github.com/microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work](https://github.com/microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work)
//
// Note: the prototype must also be set on any classes which extend this one
this.__proto__ = AlreadyReportedError.prototype; // eslint-disable-line @typescript-eslint/no-explicit-any
}
static [Symbol.hasInstance](instance) {
return TypeUuid_1.TypeUuid.isInstanceOf(instance, uuidAlreadyReportedError);
}
}
exports.AlreadyReportedError = AlreadyReportedError;
TypeUuid_1.TypeUuid.registerClass(AlreadyReportedError, uuidAlreadyReportedError);
//# sourceMappingURL=AlreadyReportedError.js.map
@@ -0,0 +1 @@
{"version":3,"file":"AlreadyReportedError.js","sourceRoot":"","sources":["../src/AlreadyReportedError.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D,yCAAsC;AAEtC,MAAM,wBAAwB,GAAW,sCAAsC,CAAC;AAEhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAa,oBAAqB,SAAQ,KAAK;IAC7C;QACE,KAAK,CAAC,oBAAoB,CAAC,CAAC;QAE5B,sGAAsG;QACtG,2RAA2R;QAC3R,EAAE;QACF,4EAA4E;QAC3E,IAAY,CAAC,SAAS,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC,yDAAyD;IACrH,CAAC;IAEM,MAAM,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,QAAgB;QACjD,OAAO,mBAAQ,CAAC,YAAY,CAAC,QAAQ,EAAE,wBAAwB,CAAC,CAAC;IACnE,CAAC;CACF;AAdD,oDAcC;AAED,mBAAQ,CAAC,aAAa,CAAC,oBAAoB,EAAE,wBAAwB,CAAC,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { TypeUuid } from './TypeUuid';\n\nconst uuidAlreadyReportedError: string = 'f26b0640-a49b-49d1-9ead-1a516d5920c7';\n\n/**\n * This exception can be thrown to indicate that an operation failed and an error message has already\n * been reported appropriately. Thus, the catch handler does not have responsibility for reporting\n * the error.\n *\n * @remarks\n * For example, suppose a tool writes interactive output to `console.log()`. When an exception is thrown,\n * the `catch` handler will typically provide simplistic reporting such as this:\n *\n * ```ts\n * catch (error) {\n * console.log(\"ERROR: \" + error.message);\n * }\n * ```\n *\n * Suppose that the code performing the operation normally prints rich output to the console. It may be able to\n * present an error message more nicely (for example, as part of a table, or structured log format). Throwing\n * `AlreadyReportedError` provides a way to use exception handling to abort the operation, but instruct the `catch`\n * handler not to print an error a second time:\n *\n * ```ts\n * catch (error) {\n * if (error instanceof AlreadyReportedError) {\n * return;\n * }\n * console.log(\"ERROR: \" + error.message);\n * }\n * ```\n *\n * @public\n */\nexport class AlreadyReportedError extends Error {\n public constructor() {\n super('An error occurred.');\n\n // Manually set the prototype, as we can no longer extend built-in classes like Error, Array, Map, etc\n // [https://github.com/microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work](https://github.com/microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work)\n //\n // Note: the prototype must also be set on any classes which extend this one\n (this as any).__proto__ = AlreadyReportedError.prototype; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n\n public static [Symbol.hasInstance](instance: object): boolean {\n return TypeUuid.isInstanceOf(instance, uuidAlreadyReportedError);\n }\n}\n\nTypeUuid.registerClass(AlreadyReportedError, uuidAlreadyReportedError);\n"]}
+200
View File
@@ -0,0 +1,200 @@
/**
* Options for controlling the parallelism of asynchronous operations.
*
* @remarks
* Used with {@link (Async:class).(mapAsync:1)}, {@link (Async:class).(mapAsync:2)} and
* {@link (Async:class).(forEachAsync:1)}, and {@link (Async:class).(forEachAsync:2)}.
*
* @public
*/
export interface IAsyncParallelismOptions {
/**
* Optionally used with the {@link (Async:class).(mapAsync:1)}, {@link (Async:class).(mapAsync:2)} and
* {@link (Async:class).(forEachAsync:1)}, and {@link (Async:class).(forEachAsync:2)} to limit the maximum
* number of concurrent promises to the specified number.
*/
concurrency?: number;
/**
* Optionally used with the {@link (Async:class).(forEachAsync:2)} to enable weighted operations where an operation can
* take up more or less than one concurrency unit.
*/
weighted?: boolean;
}
/**
* @remarks
* Used with {@link Async.runWithRetriesAsync}.
*
* @public
*/
export interface IRunWithRetriesOptions<TResult> {
/**
* The action to be performed. The action is repeatedly executed until it completes without throwing or the
* maximum number of retries is reached.
*
* @param retryCount - The number of times the action has been retried.
*/
action: (retryCount: number) => Promise<TResult> | TResult;
/**
* The maximum number of times the action should be retried.
*/
maxRetries: number;
/**
* The delay in milliseconds between retries.
*/
retryDelayMs?: number;
}
/**
* @remarks
* Used with {@link (Async:class).(forEachAsync:2)} and {@link (Async:class).(mapAsync:2)}.
*
* @public
*/
export interface IWeighted {
/**
* The weight of the element, used to determine the concurrency units that it will take up.
* Must be a whole number greater than or equal to 0.
*/
weight: number;
}
/**
* Utilities for parallel asynchronous operations, for use with the system `Promise` APIs.
*
* @public
*/
export declare class Async {
/**
* Given an input array and a `callback` function, invoke the callback to start a
* promise for each element in the array. Returns an array containing the results.
*
* @remarks
* This API is similar to the system `Array#map`, except that the loop is asynchronous,
* and the maximum number of concurrent promises can be throttled
* using {@link IAsyncParallelismOptions.concurrency}.
*
* If `callback` throws a synchronous exception, or if it returns a promise that rejects,
* then the loop stops immediately. Any remaining array items will be skipped, and
* overall operation will reject with the first error that was encountered.
*
* @param iterable - the array of inputs for the callback function
* @param callback - a function that starts an asynchronous promise for an element
* from the array
* @param options - options for customizing the control flow
* @returns an array containing the result for each callback, in the same order
* as the original input `array`
*/
static mapAsync<TEntry, TRetVal>(iterable: Iterable<TEntry> | AsyncIterable<TEntry>, callback: (entry: TEntry, arrayIndex: number) => Promise<TRetVal>, options?: (IAsyncParallelismOptions & {
weighted?: false;
}) | undefined): Promise<TRetVal[]>;
/**
* Given an input array and a `callback` function, invoke the callback to start a
* promise for each element in the array. Returns an array containing the results.
*
* @remarks
* This API is similar to the system `Array#map`, except that the loop is asynchronous,
* and the maximum number of concurrent units can be throttled
* using {@link IAsyncParallelismOptions.concurrency}. Using the {@link IAsyncParallelismOptions.weighted}
* option, the weight of each operation can be specified, which determines how many concurrent units it takes up.
*
* If `callback` throws a synchronous exception, or if it returns a promise that rejects,
* then the loop stops immediately. Any remaining array items will be skipped, and
* overall operation will reject with the first error that was encountered.
*
* @param iterable - the array of inputs for the callback function
* @param callback - a function that starts an asynchronous promise for an element
* from the array
* @param options - options for customizing the control flow
* @returns an array containing the result for each callback, in the same order
* as the original input `array`
*/
static mapAsync<TEntry extends IWeighted, TRetVal>(iterable: Iterable<TEntry> | AsyncIterable<TEntry>, callback: (entry: TEntry, arrayIndex: number) => Promise<TRetVal>, options: IAsyncParallelismOptions & {
weighted: true;
}): Promise<TRetVal[]>;
private static _forEachWeightedAsync;
/**
* Given an input array and a `callback` function, invoke the callback to start a
* promise for each element in the array.
*
* @remarks
* This API is similar to the system `Array#forEach`, except that the loop is asynchronous,
* and the maximum number of concurrent promises can be throttled
* using {@link IAsyncParallelismOptions.concurrency}.
*
* If `callback` throws a synchronous exception, or if it returns a promise that rejects,
* then the loop stops immediately. Any remaining array items will be skipped, and
* overall operation will reject with the first error that was encountered.
*
* @param iterable - the array of inputs for the callback function
* @param callback - a function that starts an asynchronous promise for an element
* from the array
* @param options - options for customizing the control flow
*/
static forEachAsync<TEntry>(iterable: Iterable<TEntry> | AsyncIterable<TEntry>, callback: (entry: TEntry, arrayIndex: number) => Promise<void>, options?: (IAsyncParallelismOptions & {
weighted?: false;
}) | undefined): Promise<void>;
/**
* Given an input array and a `callback` function, invoke the callback to start a
* promise for each element in the array.
*
* @remarks
* This API is similar to the other `Array#forEachAsync`, except that each item can have
* a weight that determines how many concurrent operations are allowed. The unweighted
* `Array#forEachAsync` is a special case of this method where weight = 1 for all items.
*
* The maximum number of concurrent operations can still be throttled using
* {@link IAsyncParallelismOptions.concurrency}, however it no longer determines the
* maximum number of operations that can be in progress at once. Instead, it determines the
* number of concurrency units that can be in progress at once. The weight of each operation
* determines how many concurrency units it takes up. For example, if the concurrency is 2
* and the first operation has a weight of 2, then only one more operation can be in progress.
*
* If `callback` throws a synchronous exception, or if it returns a promise that rejects,
* then the loop stops immediately. Any remaining array items will be skipped, and
* overall operation will reject with the first error that was encountered.
*
* @param iterable - the array of inputs for the callback function
* @param callback - a function that starts an asynchronous promise for an element
* from the array
* @param options - options for customizing the control flow
*/
static forEachAsync<TEntry extends IWeighted>(iterable: Iterable<TEntry> | AsyncIterable<TEntry>, callback: (entry: TEntry, arrayIndex: number) => Promise<void>, options: IAsyncParallelismOptions & {
weighted: true;
}): Promise<void>;
/**
* Return a promise that resolves after the specified number of milliseconds.
*/
static sleepAsync(ms: number): Promise<void>;
/**
* Executes an async function and optionally retries it if it fails.
*/
static runWithRetriesAsync<TResult>({ action, maxRetries, retryDelayMs }: IRunWithRetriesOptions<TResult>): Promise<TResult>;
/**
* Ensures that the argument is a valid {@link IWeighted}, with a `weight` argument that
* is a positive integer or 0.
*/
static validateWeightedIterable(operation: IWeighted): void;
/**
* Returns a Signal, a.k.a. a "deferred promise".
*/
static getSignal(): [Promise<void>, () => void, (err: Error) => void];
}
/**
* A queue that allows for asynchronous iteration. During iteration, the queue will wait until
* the next item is pushed into the queue before yielding. If instead all queue items are consumed
* and all callbacks have been called, the queue will return.
*
* @public
*/
export declare class AsyncQueue<T> implements AsyncIterable<[T, () => void]> {
private _queue;
private _onPushSignal;
private _onPushResolve;
constructor(iterable?: Iterable<T>);
[Symbol.asyncIterator](): AsyncIterableIterator<[T, () => void]>;
/**
* Adds an item to the queue.
*
* @param item - The item to push into the queue.
*/
push(item: T): void;
}
//# sourceMappingURL=Async.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Async.d.ts","sourceRoot":"","sources":["../src/Async.ts"],"names":[],"mappings":"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"}
+236
View File
@@ -0,0 +1,236 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), i, q = [];
return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsyncQueue = exports.Async = void 0;
function toWeightedIterator(iterable, useWeights) {
const iterator = (iterable[Symbol.iterator] ||
iterable[Symbol.asyncIterator]).call(iterable);
return {
[Symbol.asyncIterator]: () => ({
// eslint-disable-next-line @typescript-eslint/naming-convention
next: async () => {
// The await is necessary here, but TS will complain - it's a false positive.
const { value, done } = await iterator.next();
return {
value: { element: value, weight: useWeights ? value === null || value === void 0 ? void 0 : value.weight : 1 },
done: !!done
};
}
})
};
}
/**
* Utilities for parallel asynchronous operations, for use with the system `Promise` APIs.
*
* @public
*/
class Async {
static async mapAsync(iterable, callback, options) {
const result = [];
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/22609, it succeeds against the implementation but fails against the overloads
await Async.forEachAsync(iterable, async (item, arrayIndex) => {
result[arrayIndex] = await callback(item, arrayIndex);
}, options);
return result;
}
static async _forEachWeightedAsync(iterable, callback, options) {
await new Promise((resolve, reject) => {
const concurrency = (options === null || options === void 0 ? void 0 : options.concurrency) && options.concurrency > 0 ? options.concurrency : Infinity;
let concurrentUnitsInProgress = 0;
const iterator = iterable[Symbol.asyncIterator].call(iterable);
let arrayIndex = 0;
let iteratorIsComplete = false;
let promiseHasResolvedOrRejected = false;
async function queueOperationsAsync() {
while (concurrentUnitsInProgress < concurrency &&
!iteratorIsComplete &&
!promiseHasResolvedOrRejected) {
// Increment the current concurrency units in progress by the concurrency limit before fetching the iterator weight.
// This function is reentrant, so this if concurrency is finite, at most 1 operation will be waiting. If it's infinite,
// there will be effectively no cap on the number of operations waiting.
const limitedConcurrency = !Number.isFinite(concurrency) ? 1 : concurrency;
concurrentUnitsInProgress += limitedConcurrency;
const currentIteratorResult = await iterator.next();
// eslint-disable-next-line require-atomic-updates
iteratorIsComplete = !!currentIteratorResult.done;
if (!iteratorIsComplete) {
const currentIteratorValue = currentIteratorResult.value;
Async.validateWeightedIterable(currentIteratorValue);
// Cap the weight to concurrency, this allows 0 weight items to execute despite the concurrency limit.
const weight = Math.min(currentIteratorValue.weight, concurrency);
// Remove the "lock" from the concurrency check and only apply the current weight.
// This should allow other operations to execute.
concurrentUnitsInProgress += weight;
concurrentUnitsInProgress -= limitedConcurrency;
Promise.resolve(callback(currentIteratorValue.element, arrayIndex++))
.then(async () => {
// Remove the operation completely from the in progress units.
concurrentUnitsInProgress -= weight;
await onOperationCompletionAsync();
})
.catch((error) => {
promiseHasResolvedOrRejected = true;
reject(error);
});
}
else {
// The iterator is complete and there wasn't a value, so untrack the waiting state.
concurrentUnitsInProgress -= limitedConcurrency;
}
}
if (iteratorIsComplete) {
await onOperationCompletionAsync();
}
}
async function onOperationCompletionAsync() {
if (!promiseHasResolvedOrRejected) {
if (concurrentUnitsInProgress === 0 && iteratorIsComplete) {
promiseHasResolvedOrRejected = true;
resolve();
}
else if (!iteratorIsComplete) {
await queueOperationsAsync();
}
}
}
queueOperationsAsync().catch((error) => {
promiseHasResolvedOrRejected = true;
reject(error);
});
});
}
static async forEachAsync(iterable, callback, options) {
await Async._forEachWeightedAsync(toWeightedIterator(iterable, options === null || options === void 0 ? void 0 : options.weighted), callback, options);
}
/**
* Return a promise that resolves after the specified number of milliseconds.
*/
static async sleepAsync(ms) {
await new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
/**
* Executes an async function and optionally retries it if it fails.
*/
static async runWithRetriesAsync({ action, maxRetries, retryDelayMs = 0 }) {
let retryCount = 0;
// eslint-disable-next-line no-constant-condition
while (true) {
try {
return await action(retryCount);
}
catch (e) {
if (++retryCount > maxRetries) {
throw e;
}
else if (retryDelayMs > 0) {
await Async.sleepAsync(retryDelayMs);
}
}
}
}
/**
* Ensures that the argument is a valid {@link IWeighted}, with a `weight` argument that
* is a positive integer or 0.
*/
static validateWeightedIterable(operation) {
if (operation.weight < 0) {
throw new Error('Weight must be a whole number greater than or equal to 0');
}
if (operation.weight % 1 !== 0) {
throw new Error('Weight must be a whole number greater than or equal to 0');
}
}
/**
* Returns a Signal, a.k.a. a "deferred promise".
*/
static getSignal() {
return getSignal();
}
}
exports.Async = Async;
/**
* Returns an unwrapped promise.
*/
function getSignal() {
let resolver;
let rejecter;
const promise = new Promise((resolve, reject) => {
resolver = resolve;
rejecter = reject;
});
return [promise, resolver, rejecter];
}
/**
* A queue that allows for asynchronous iteration. During iteration, the queue will wait until
* the next item is pushed into the queue before yielding. If instead all queue items are consumed
* and all callbacks have been called, the queue will return.
*
* @public
*/
class AsyncQueue {
constructor(iterable) {
this._queue = iterable ? Array.from(iterable) : [];
const [promise, resolver] = getSignal();
this._onPushSignal = promise;
this._onPushResolve = resolver;
}
[Symbol.asyncIterator]() {
return __asyncGenerator(this, arguments, function* _a() {
let activeIterations = 0;
let [callbackSignal, callbackResolve] = getSignal();
const callback = () => {
if (--activeIterations === 0) {
// Resolve whatever the latest callback promise is and create a new one
callbackResolve();
const [newCallbackSignal, newCallbackResolve] = getSignal();
callbackSignal = newCallbackSignal;
callbackResolve = newCallbackResolve;
}
};
let position = 0;
while (this._queue.length > position || activeIterations > 0) {
if (this._queue.length > position) {
activeIterations++;
yield yield __await([this._queue[position++], callback]);
}
else {
// On push, the item will be added to the queue and the onPushSignal will be resolved.
// On calling the callback, active iterations will be decremented by the callback and the
// callbackSignal will be resolved. This means that the loop will continue if there are
// active iterations or if there are items in the queue that haven't been yielded yet.
yield __await(Promise.race([this._onPushSignal, callbackSignal]));
}
}
});
}
/**
* Adds an item to the queue.
*
* @param item - The item to push into the queue.
*/
push(item) {
this._queue.push(item);
this._onPushResolve();
const [onPushSignal, onPushResolve] = getSignal();
this._onPushSignal = onPushSignal;
this._onPushResolve = onPushResolve;
}
}
exports.AsyncQueue = AsyncQueue;
//# sourceMappingURL=Async.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Async.js","sourceRoot":"","sources":["../src/Async.ts"],"names":[],"mappings":"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 truncated
+27
View File
@@ -0,0 +1,27 @@
/**
* String constants for common filenames and parts of filenames.
*
* @public
*/
export declare const FileConstants: {
/**
* "package.json" - the configuration file that defines an NPM package
*/
readonly PackageJson: "package.json";
};
/**
* String constants for common folder names.
*
* @public
*/
export declare const FolderConstants: {
/**
* ".git" - the data storage for a Git working folder
*/
readonly Git: ".git";
/**
* "node_modules" - the folder where package managers install their files
*/
readonly NodeModules: "node_modules";
};
//# sourceMappingURL=Constants.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"Constants.d.ts","sourceRoot":"","sources":["../src/Constants.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AAEH,eAAO,MAAM,aAAa;IACxB;;OAEG;;CAEK,CAAC;AAEX;;;;GAIG;AAEH,eAAO,MAAM,eAAe;IAC1B;;OAEG;;IAGH;;OAEG;;CAEK,CAAC"}
+34
View File
@@ -0,0 +1,34 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.FolderConstants = exports.FileConstants = void 0;
/**
* String constants for common filenames and parts of filenames.
*
* @public
*/
// eslint-disable-next-line @typescript-eslint/typedef
exports.FileConstants = {
/**
* "package.json" - the configuration file that defines an NPM package
*/
PackageJson: 'package.json'
};
/**
* String constants for common folder names.
*
* @public
*/
// eslint-disable-next-line @typescript-eslint/typedef
exports.FolderConstants = {
/**
* ".git" - the data storage for a Git working folder
*/
Git: '.git',
/**
* "node_modules" - the folder where package managers install their files
*/
NodeModules: 'node_modules'
};
//# sourceMappingURL=Constants.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Constants.js","sourceRoot":"","sources":["../src/Constants.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D;;;;GAIG;AACH,sDAAsD;AACzC,QAAA,aAAa,GAAG;IAC3B;;OAEG;IACH,WAAW,EAAE,cAAc;CACnB,CAAC;AAEX;;;;GAIG;AACH,sDAAsD;AACzC,QAAA,eAAe,GAAG;IAC7B;;OAEG;IACH,GAAG,EAAE,MAAM;IAEX;;OAEG;IACH,WAAW,EAAE,cAAc;CACnB,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n/**\n * String constants for common filenames and parts of filenames.\n *\n * @public\n */\n// eslint-disable-next-line @typescript-eslint/typedef\nexport const FileConstants = {\n /**\n * \"package.json\" - the configuration file that defines an NPM package\n */\n PackageJson: 'package.json'\n} as const;\n\n/**\n * String constants for common folder names.\n *\n * @public\n */\n// eslint-disable-next-line @typescript-eslint/typedef\nexport const FolderConstants = {\n /**\n * \".git\" - the data storage for a Git working folder\n */\n Git: '.git',\n\n /**\n * \"node_modules\" - the folder where package managers install their files\n */\n NodeModules: 'node_modules'\n} as const;\n"]}
+120
View File
@@ -0,0 +1,120 @@
/**
* A helper for looking up TypeScript `enum` keys/values.
*
* @remarks
* TypeScript enums implement a lookup table for mapping between their keys and values:
*
* ```ts
* enum Colors {
* Red = 1
* }
*
* // Prints "Red"
* console.log(Colors[1]);
*
* // Prints "1"
* console.log(Colors["Red]);
* ```
*
* However the compiler's "noImplicitAny" validation has trouble with these mappings, because
* there are so many possible types for the map elements:
*
* ```ts
* function f(s: string): Colors | undefined {
* // (TS 7015) Element implicitly has an 'any' type because
* // index expression is not of type 'number'.
* return Colors[s];
* }
* ```
*
* The `Enum` helper provides a more specific, strongly typed way to access members:
*
* ```ts
* function f(s: string): Colors | undefined {
* return Enum.tryGetValueByKey(Colors, s);
* }
* ```
*
* @public
*/
export declare class Enum {
private constructor();
/**
* Returns an enum value, given its key. Returns `undefined` if no matching key is found.
*
* @example
*
* Example usage:
* ```ts
* enum Colors {
* Red = 1
* }
*
* // Prints "1"
* console.log(Enum.tryGetValueByKey(Colors, "Red"));
*
* // Prints "undefined"
* console.log(Enum.tryGetValueByKey(Colors, "Black"));
* ```
*/
static tryGetValueByKey<TEnumValue>(enumObject: {
[key: string]: TEnumValue | string;
[key: number]: TEnumValue | string;
}, key: string): TEnumValue | undefined;
/**
* This API is similar to {@link Enum.tryGetValueByKey}, except that it throws an exception
* if the key is undefined.
*/
static getValueByKey<TEnumValue>(enumObject: {
[key: string]: TEnumValue | string;
[key: number]: TEnumValue | string;
}, key: string): TEnumValue;
/**
* Returns an enum string key, given its numeric value. Returns `undefined` if no matching value
* is found.
*
* @remarks
* The TypeScript compiler only creates a reverse mapping for enum members whose value is numeric.
* For example:
*
* ```ts
* enum E {
* A = 1,
* B = 'c'
* }
*
* // Prints "A"
* console.log(E[1]);
*
* // Prints "undefined"
* console.log(E["c"]);
* ```
*
* @example
*
* Example usage:
* ```ts
* enum Colors {
* Red = 1,
* Blue = 'blue'
* }
*
* // Prints "Red"
* console.log(Enum.tryGetKeyByNumber(Colors, 1));
*
* // Prints "undefined"
* console.log(Enum.tryGetKeyByNumber(Colors, -1));
* ```
*/
static tryGetKeyByNumber<TEnumValue, TEnumObject extends {
[key: string]: TEnumValue;
}>(enumObject: TEnumObject, value: number): keyof typeof enumObject | undefined;
/**
* This API is similar to {@link Enum.tryGetKeyByNumber}, except that it throws an exception
* if the key is undefined.
*/
static getKeyByNumber<TEnumValue, TEnumObject extends {
[key: string]: TEnumValue;
}>(enumObject: TEnumObject, value: number): keyof typeof enumObject;
}
//# sourceMappingURL=Enum.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Enum.d.ts","sourceRoot":"","sources":["../src/Enum.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,qBAAa,IAAI;IACf,OAAO;IAEP;;;;;;;;;;;;;;;;;OAiBG;WACW,gBAAgB,CAAC,UAAU,EACvC,UAAU,EAAE;QACV,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;QACnC,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;KACpC,EACD,GAAG,EAAE,MAAM,GACV,UAAU,GAAG,SAAS;IAKzB;;;OAGG;WACW,aAAa,CAAC,UAAU,EACpC,UAAU,EAAE;QACV,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;QACnC,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;KACpC,EACD,GAAG,EAAE,MAAM,GACV,UAAU;IASb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoCG;WACW,iBAAiB,CAAC,UAAU,EAAE,WAAW,SAAS;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,EAC3F,UAAU,EAAE,WAAW,EACvB,KAAK,EAAE,MAAM,GACZ,MAAM,OAAO,UAAU,GAAG,SAAS;IAKtC;;;OAGG;WACW,cAAc,CAAC,UAAU,EAAE,WAAW,SAAS;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,EACxF,UAAU,EAAE,WAAW,EACvB,KAAK,EAAE,MAAM,GACZ,MAAM,OAAO,UAAU;CAQ3B"}
+136
View File
@@ -0,0 +1,136 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.Enum = void 0;
/**
* A helper for looking up TypeScript `enum` keys/values.
*
* @remarks
* TypeScript enums implement a lookup table for mapping between their keys and values:
*
* ```ts
* enum Colors {
* Red = 1
* }
*
* // Prints "Red"
* console.log(Colors[1]);
*
* // Prints "1"
* console.log(Colors["Red]);
* ```
*
* However the compiler's "noImplicitAny" validation has trouble with these mappings, because
* there are so many possible types for the map elements:
*
* ```ts
* function f(s: string): Colors | undefined {
* // (TS 7015) Element implicitly has an 'any' type because
* // index expression is not of type 'number'.
* return Colors[s];
* }
* ```
*
* The `Enum` helper provides a more specific, strongly typed way to access members:
*
* ```ts
* function f(s: string): Colors | undefined {
* return Enum.tryGetValueByKey(Colors, s);
* }
* ```
*
* @public
*/
class Enum {
constructor() { }
/**
* Returns an enum value, given its key. Returns `undefined` if no matching key is found.
*
* @example
*
* Example usage:
* ```ts
* enum Colors {
* Red = 1
* }
*
* // Prints "1"
* console.log(Enum.tryGetValueByKey(Colors, "Red"));
*
* // Prints "undefined"
* console.log(Enum.tryGetValueByKey(Colors, "Black"));
* ```
*/
static tryGetValueByKey(enumObject, key) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return enumObject[key];
}
/**
* This API is similar to {@link Enum.tryGetValueByKey}, except that it throws an exception
* if the key is undefined.
*/
static getValueByKey(enumObject, key) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const result = enumObject[key];
if (result === undefined) {
throw new Error(`The lookup key ${JSON.stringify(key)} is not defined`);
}
return result;
}
/**
* Returns an enum string key, given its numeric value. Returns `undefined` if no matching value
* is found.
*
* @remarks
* The TypeScript compiler only creates a reverse mapping for enum members whose value is numeric.
* For example:
*
* ```ts
* enum E {
* A = 1,
* B = 'c'
* }
*
* // Prints "A"
* console.log(E[1]);
*
* // Prints "undefined"
* console.log(E["c"]);
* ```
*
* @example
*
* Example usage:
* ```ts
* enum Colors {
* Red = 1,
* Blue = 'blue'
* }
*
* // Prints "Red"
* console.log(Enum.tryGetKeyByNumber(Colors, 1));
*
* // Prints "undefined"
* console.log(Enum.tryGetKeyByNumber(Colors, -1));
* ```
*/
static tryGetKeyByNumber(enumObject, value) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return enumObject[value];
}
/**
* This API is similar to {@link Enum.tryGetKeyByNumber}, except that it throws an exception
* if the key is undefined.
*/
static getKeyByNumber(enumObject, value) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const result = enumObject[value];
if (result === undefined) {
throw new Error(`The value ${value} does not exist in the mapping`);
}
return result;
}
}
exports.Enum = Enum;
//# sourceMappingURL=Enum.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Enum.js","sourceRoot":"","sources":["../src/Enum.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAa,IAAI;IACf,gBAAuB,CAAC;IAExB;;;;;;;;;;;;;;;;;OAiBG;IACI,MAAM,CAAC,gBAAgB,CAC5B,UAGC,EACD,GAAW;QAEX,8DAA8D;QAC9D,OAAO,UAAU,CAAC,GAAG,CAAQ,CAAC;IAChC,CAAC;IAED;;;OAGG;IACI,MAAM,CAAC,aAAa,CACzB,UAGC,EACD,GAAW;QAEX,8DAA8D;QAC9D,MAAM,MAAM,GAA2B,UAAU,CAAC,GAAG,CAAQ,CAAC;QAC9D,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoCG;IACI,MAAM,CAAC,iBAAiB,CAC7B,UAAuB,EACvB,KAAa;QAEb,8DAA8D;QAC9D,OAAO,UAAU,CAAC,KAAK,CAAQ,CAAC;IAClC,CAAC;IAED;;;OAGG;IACI,MAAM,CAAC,cAAc,CAC1B,UAAuB,EACvB,KAAa;QAEb,8DAA8D;QAC9D,MAAM,MAAM,GAAwC,UAAU,CAAC,KAAK,CAAQ,CAAC;QAC7E,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,aAAa,KAAK,gCAAgC,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AA/GD,oBA+GC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n/**\n * A helper for looking up TypeScript `enum` keys/values.\n *\n * @remarks\n * TypeScript enums implement a lookup table for mapping between their keys and values:\n *\n * ```ts\n * enum Colors {\n * Red = 1\n * }\n *\n * // Prints \"Red\"\n * console.log(Colors[1]);\n *\n * // Prints \"1\"\n * console.log(Colors[\"Red]);\n * ```\n *\n * However the compiler's \"noImplicitAny\" validation has trouble with these mappings, because\n * there are so many possible types for the map elements:\n *\n * ```ts\n * function f(s: string): Colors | undefined {\n * // (TS 7015) Element implicitly has an 'any' type because\n * // index expression is not of type 'number'.\n * return Colors[s];\n * }\n * ```\n *\n * The `Enum` helper provides a more specific, strongly typed way to access members:\n *\n * ```ts\n * function f(s: string): Colors | undefined {\n * return Enum.tryGetValueByKey(Colors, s);\n * }\n * ```\n *\n * @public\n */\nexport class Enum {\n private constructor() {}\n\n /**\n * Returns an enum value, given its key. Returns `undefined` if no matching key is found.\n *\n * @example\n *\n * Example usage:\n * ```ts\n * enum Colors {\n * Red = 1\n * }\n *\n * // Prints \"1\"\n * console.log(Enum.tryGetValueByKey(Colors, \"Red\"));\n *\n * // Prints \"undefined\"\n * console.log(Enum.tryGetValueByKey(Colors, \"Black\"));\n * ```\n */\n public static tryGetValueByKey<TEnumValue>(\n enumObject: {\n [key: string]: TEnumValue | string;\n [key: number]: TEnumValue | string;\n },\n key: string\n ): TEnumValue | undefined {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return enumObject[key] as any;\n }\n\n /**\n * This API is similar to {@link Enum.tryGetValueByKey}, except that it throws an exception\n * if the key is undefined.\n */\n public static getValueByKey<TEnumValue>(\n enumObject: {\n [key: string]: TEnumValue | string;\n [key: number]: TEnumValue | string;\n },\n key: string\n ): TEnumValue {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result: TEnumValue | undefined = enumObject[key] as any;\n if (result === undefined) {\n throw new Error(`The lookup key ${JSON.stringify(key)} is not defined`);\n }\n return result;\n }\n\n /**\n * Returns an enum string key, given its numeric value. Returns `undefined` if no matching value\n * is found.\n *\n * @remarks\n * The TypeScript compiler only creates a reverse mapping for enum members whose value is numeric.\n * For example:\n *\n * ```ts\n * enum E {\n * A = 1,\n * B = 'c'\n * }\n *\n * // Prints \"A\"\n * console.log(E[1]);\n *\n * // Prints \"undefined\"\n * console.log(E[\"c\"]);\n * ```\n *\n * @example\n *\n * Example usage:\n * ```ts\n * enum Colors {\n * Red = 1,\n * Blue = 'blue'\n * }\n *\n * // Prints \"Red\"\n * console.log(Enum.tryGetKeyByNumber(Colors, 1));\n *\n * // Prints \"undefined\"\n * console.log(Enum.tryGetKeyByNumber(Colors, -1));\n * ```\n */\n public static tryGetKeyByNumber<TEnumValue, TEnumObject extends { [key: string]: TEnumValue }>(\n enumObject: TEnumObject,\n value: number\n ): keyof typeof enumObject | undefined {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return enumObject[value] as any;\n }\n\n /**\n * This API is similar to {@link Enum.tryGetKeyByNumber}, except that it throws an exception\n * if the key is undefined.\n */\n public static getKeyByNumber<TEnumValue, TEnumObject extends { [key: string]: TEnumValue }>(\n enumObject: TEnumObject,\n value: number\n ): keyof typeof enumObject {\n // eslint-disable-next-line @typescript-eslint/no-Line truncated
@@ -0,0 +1,72 @@
/**
* A process environment variable name and its value. Used by {@link EnvironmentMap}.
* @public
*/
export interface IEnvironmentEntry {
/**
* The name of the environment variable.
*/
name: string;
/**
* The value of the environment variable.
*/
value: string;
}
/**
* A map data structure that stores process environment variables. On Windows
* operating system, the variable names are case-insensitive.
* @public
*/
export declare class EnvironmentMap {
private readonly _map;
/**
* Whether the environment variable names are case-sensitive.
*
* @remarks
* On Windows operating system, environment variables are case-insensitive.
* The map will preserve the variable name casing from the most recent assignment operation.
*/
readonly caseSensitive: boolean;
constructor(environmentObject?: Record<string, string | undefined>);
/**
* Clears all entries, resulting in an empty map.
*/
clear(): void;
/**
* Assigns the variable to the specified value. A previous value will be overwritten.
*
* @remarks
* The value can be an empty string. To completely remove the entry, use
* {@link EnvironmentMap.unset} instead.
*/
set(name: string, value: string): void;
/**
* Removes the key from the map, if present.
*/
unset(name: string): void;
/**
* Returns the value of the specified variable, or `undefined` if the map does not contain that name.
*/
get(name: string): string | undefined;
/**
* Returns the map keys, which are environment variable names.
*/
names(): IterableIterator<string>;
/**
* Returns the map entries.
*/
entries(): IterableIterator<IEnvironmentEntry>;
/**
* Adds each entry from `environmentMap` to this map.
*/
mergeFrom(environmentMap: EnvironmentMap): void;
/**
* Merges entries from a plain JavaScript object, such as would be used with the `process.env` API.
*/
mergeFromObject(environmentObject?: Record<string, string | undefined>): void;
/**
* Returns the keys as a plain JavaScript object similar to the object returned by the `process.env` API.
*/
toObject(): Record<string, string>;
}
//# sourceMappingURL=EnvironmentMap.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"EnvironmentMap.d.ts","sourceRoot":"","sources":["../src/EnvironmentMap.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6C;IAElE;;;;;;OAMG;IACH,SAAgB,aAAa,EAAE,OAAO,CAAC;gBAEpB,iBAAiB,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAM;IAe7E;;OAEG;IACI,KAAK,IAAI,IAAI;IAIpB;;;;;;OAMG;IACI,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAK7C;;OAEG;IACI,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKhC;;OAEG;IACI,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAS5C;;OAEG;IACI,KAAK,IAAI,gBAAgB,CAAC,MAAM,CAAC;IAIxC;;OAEG;IACI,OAAO,IAAI,gBAAgB,CAAC,iBAAiB,CAAC;IAIrD;;OAEG;IACI,SAAS,CAAC,cAAc,EAAE,cAAc,GAAG,IAAI;IAMtD;;OAEG;IACI,eAAe,CAAC,iBAAiB,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAM,GAAG,IAAI;IAQxF;;OAEG;IACI,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;CAO1C"}
+108
View File
@@ -0,0 +1,108 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EnvironmentMap = void 0;
const process_1 = __importDefault(require("process"));
const InternalError_1 = require("./InternalError");
/**
* A map data structure that stores process environment variables. On Windows
* operating system, the variable names are case-insensitive.
* @public
*/
class EnvironmentMap {
constructor(environmentObject = {}) {
this._map = new Map();
// This property helps catch a mistake where an instance of `EnvironmentMap` is accidentally passed to
// a function that expects a `Record<string, string>` (as would be used with the `process.env` API).
// The property getter will throw an exception if that function tries to enumerate the object values.
Object.defineProperty(this, '_sanityCheck', {
enumerable: true,
get: function () {
throw new InternalError_1.InternalError('Attempt to read EnvironmentMap class as an object');
}
});
this.caseSensitive = process_1.default.platform !== 'win32';
this.mergeFromObject(environmentObject);
}
/**
* Clears all entries, resulting in an empty map.
*/
clear() {
this._map.clear();
}
/**
* Assigns the variable to the specified value. A previous value will be overwritten.
*
* @remarks
* The value can be an empty string. To completely remove the entry, use
* {@link EnvironmentMap.unset} instead.
*/
set(name, value) {
const key = this.caseSensitive ? name : name.toUpperCase();
this._map.set(key, { name: name, value });
}
/**
* Removes the key from the map, if present.
*/
unset(name) {
const key = this.caseSensitive ? name : name.toUpperCase();
this._map.delete(key);
}
/**
* Returns the value of the specified variable, or `undefined` if the map does not contain that name.
*/
get(name) {
const key = this.caseSensitive ? name : name.toUpperCase();
const entry = this._map.get(key);
if (entry === undefined) {
return undefined;
}
return entry.value;
}
/**
* Returns the map keys, which are environment variable names.
*/
names() {
return this._map.keys();
}
/**
* Returns the map entries.
*/
entries() {
return this._map.values();
}
/**
* Adds each entry from `environmentMap` to this map.
*/
mergeFrom(environmentMap) {
for (const entry of environmentMap.entries()) {
this.set(entry.name, entry.value);
}
}
/**
* Merges entries from a plain JavaScript object, such as would be used with the `process.env` API.
*/
mergeFromObject(environmentObject = {}) {
for (const [name, value] of Object.entries(environmentObject)) {
if (value !== undefined) {
this.set(name, value);
}
}
}
/**
* Returns the keys as a plain JavaScript object similar to the object returned by the `process.env` API.
*/
toObject() {
const result = {};
for (const entry of this.entries()) {
result[entry.name] = entry.value;
}
return result;
}
}
exports.EnvironmentMap = EnvironmentMap;
//# sourceMappingURL=EnvironmentMap.js.map
@@ -0,0 +1 @@
{"version":3,"file":"EnvironmentMap.js","sourceRoot":"","sources":["../src/EnvironmentMap.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport process from 'process';\nimport { InternalError } from './InternalError';\n\n/**\n * A process environment variable name and its value. Used by {@link EnvironmentMap}.\n * @public\n */\nexport interface IEnvironmentEntry {\n /**\n * The name of the environment variable.\n */\n name: string;\n\n /**\n * The value of the environment variable.\n */\n value: string;\n}\n\n/**\n * A map data structure that stores process environment variables. On Windows\n * operating system, the variable names are case-insensitive.\n * @public\n */\nexport class EnvironmentMap {\n private readonly _map: Map<string, IEnvironmentEntry> = new Map();\n\n /**\n * Whether the environment variable names are case-sensitive.\n *\n * @remarks\n * On Windows operating system, environment variables are case-insensitive.\n * The map will preserve the variable name casing from the most recent assignment operation.\n */\n public readonly caseSensitive: boolean;\n\n public constructor(environmentObject: Record<string, string | undefined> = {}) {\n // This property helps catch a mistake where an instance of `EnvironmentMap` is accidentally passed to\n // a function that expects a `Record<string, string>` (as would be used with the `process.env` API).\n // The property getter will throw an exception if that function tries to enumerate the object values.\n Object.defineProperty(this, '_sanityCheck', {\n enumerable: true,\n get: function () {\n throw new InternalError('Attempt to read EnvironmentMap class as an object');\n }\n });\n\n this.caseSensitive = process.platform !== 'win32';\n this.mergeFromObject(environmentObject);\n }\n\n /**\n * Clears all entries, resulting in an empty map.\n */\n public clear(): void {\n this._map.clear();\n }\n\n /**\n * Assigns the variable to the specified value. A previous value will be overwritten.\n *\n * @remarks\n * The value can be an empty string. To completely remove the entry, use\n * {@link EnvironmentMap.unset} instead.\n */\n public set(name: string, value: string): void {\n const key: string = this.caseSensitive ? name : name.toUpperCase();\n this._map.set(key, { name: name, value });\n }\n\n /**\n * Removes the key from the map, if present.\n */\n public unset(name: string): void {\n const key: string = this.caseSensitive ? name : name.toUpperCase();\n this._map.delete(key);\n }\n\n /**\n * Returns the value of the specified variable, or `undefined` if the map does not contain that name.\n */\n public get(name: string): string | undefined {\n const kLine truncated
+349
View File
@@ -0,0 +1,349 @@
import * as child_process from 'child_process';
import { EnvironmentMap } from './EnvironmentMap';
/**
* Typings for one of the streams inside IExecutableSpawnSyncOptions.stdio.
* @public
*/
export type ExecutableStdioStreamMapping = 'pipe' | 'ignore' | 'inherit' | NodeJS.WritableStream | NodeJS.ReadableStream | number | undefined;
/**
* Types for {@link IExecutableSpawnSyncOptions.stdio}
* and {@link IExecutableSpawnOptions.stdio}
* @public
*/
export type ExecutableStdioMapping = 'pipe' | 'ignore' | 'inherit' | ExecutableStdioStreamMapping[];
/**
* Options for Executable.tryResolve().
* @public
*/
export interface IExecutableResolveOptions {
/**
* The current working directory. If omitted, process.cwd() will be used.
*/
currentWorkingDirectory?: string;
/**
* The environment variables for the child process.
*
* @remarks
* If `environment` and `environmentMap` are both omitted, then `process.env` will be used.
* If `environment` and `environmentMap` cannot both be specified.
*/
environment?: NodeJS.ProcessEnv;
/**
* The environment variables for the child process.
*
* @remarks
* If `environment` and `environmentMap` are both omitted, then `process.env` will be used.
* If `environment` and `environmentMap` cannot both be specified.
*/
environmentMap?: EnvironmentMap;
}
/**
* Options for {@link Executable.spawnSync}
* @public
*/
export interface IExecutableSpawnSyncOptions extends IExecutableResolveOptions {
/**
* The content to be passed to the child process's stdin.
*
* NOTE: If specified, this content replaces any IExecutableSpawnSyncOptions.stdio[0]
* mapping for stdin.
*/
input?: string;
/**
* The stdio mappings for the child process.
*
* NOTE: If IExecutableSpawnSyncOptions.input is provided, it will take precedence
* over the stdin mapping (stdio[0]).
*/
stdio?: ExecutableStdioMapping;
/**
* The maximum time the process is allowed to run before it will be terminated.
*/
timeoutMs?: number;
/**
* The largest amount of bytes allowed on stdout or stderr for this synchronous operation.
* If exceeded, the child process will be terminated. The default is 200 * 1024.
*/
maxBuffer?: number;
}
/**
* Options for {@link Executable.spawn}
* @public
*/
export interface IExecutableSpawnOptions extends IExecutableResolveOptions {
/**
* The stdio mappings for the child process.
*
* NOTE: If IExecutableSpawnSyncOptions.input is provided, it will take precedence
* over the stdin mapping (stdio[0]).
*/
stdio?: ExecutableStdioMapping;
}
/**
* The options for running a process to completion using {@link Executable.(waitForExitAsync:3)}.
*
* @public
*/
export interface IWaitForExitOptions {
/**
* Whether or not to throw when the process completes with a non-zero exit code. Defaults to false.
*
* @defaultValue false
*/
throwOnNonZeroExitCode?: boolean;
/**
* Whether or not to throw when the process is terminated by a signal. Defaults to false.
*
* @defaultValue false
*/
throwOnSignal?: boolean;
/**
* The encoding of the output. If not provided, the output will not be collected.
*/
encoding?: BufferEncoding | 'buffer';
}
/**
* {@inheritDoc IWaitForExitOptions}
*
* @public
*/
export interface IWaitForExitWithStringOptions extends IWaitForExitOptions {
/**
* {@inheritDoc IWaitForExitOptions.encoding}
*/
encoding: BufferEncoding;
}
/**
* {@inheritDoc IWaitForExitOptions}
*
* @public
*/
export interface IWaitForExitWithBufferOptions extends IWaitForExitOptions {
/**
* {@inheritDoc IWaitForExitOptions.encoding}
*/
encoding: 'buffer';
}
/**
* The result of running a process to completion using {@link Executable.(waitForExitAsync:3)}.
*
* @public
*/
export interface IWaitForExitResult<T extends Buffer | string | never = never> {
/**
* The process stdout output, if encoding was specified.
*/
stdout: T;
/**
* The process stderr output, if encoding was specified.
*/
stderr: T;
/**
* The process exit code. If the process was terminated, this will be null.
*/
exitCode: number | null;
/**
* The process signal that terminated the process. If the process exited normally, this will be null.
*/
signal: string | null;
}
/**
* Process information sourced from the system. This process info is sourced differently depending
* on the operating system:
* - On Windows, this uses the `wmic.exe` utility.
* - On Unix, this uses the `ps` utility.
*
* @public
*/
export interface IProcessInfo {
/**
* The name of the process.
*
* @remarks On Windows, the process name will be empty if the process is a kernel process.
* On Unix, the process name will be empty if the process is the root process.
*/
processName: string;
/**
* The process ID.
*/
processId: number;
/**
* The parent process info.
*
* @remarks On Windows, the parent process info will be undefined if the process is a kernel process.
* On Unix, the parent process info will be undefined if the process is the root process.
*/
parentProcessInfo: IProcessInfo | undefined;
/**
* The child process infos.
*/
childProcessInfos: IProcessInfo[];
}
export declare function parseProcessListOutputAsync(stream: NodeJS.ReadableStream, platform?: NodeJS.Platform): Promise<Map<number, IProcessInfo>>;
export declare function parseProcessListOutput(output: Iterable<string | null>, platform?: NodeJS.Platform): Map<number, IProcessInfo>;
/**
* The Executable class provides a safe, portable, recommended solution for tools that need
* to launch child processes.
*
* @remarks
* The NodeJS child_process API provides a solution for launching child processes, however
* its design encourages reliance on the operating system shell for certain features.
* Invoking the OS shell is not safe, not portable, and generally not recommended:
*
* - Different shells have different behavior and command-line syntax, and which shell you
* will get with NodeJS is unpredictable. There is no universal shell guaranteed to be
* available on all platforms.
*
* - If a command parameter contains symbol characters, a shell may interpret them, which
* can introduce a security vulnerability
*
* - Each shell has different rules for escaping these symbols. On Windows, the default
* shell is incapable of escaping certain character sequences.
*
* The Executable API provides a pure JavaScript implementation of primitive shell-like
* functionality for searching the default PATH, appending default file extensions on Windows,
* and executing a file that may contain a POSIX shebang. This primitive functionality
* is sufficient (and recommended) for most tooling scenarios.
*
* If you need additional shell features such as wildcard globbing, environment variable
* expansion, piping, or built-in commands, then we recommend to use the `@microsoft/rushell`
* library instead. Rushell is a pure JavaScript shell with a standard syntax that is
* guaranteed to work consistently across all platforms.
*
* @public
*/
export declare class Executable {
/**
* Synchronously create a child process and optionally capture its output.
*
* @remarks
* This function is similar to child_process.spawnSync(). The main differences are:
*
* - It does not invoke the OS shell unless the executable file is a shell script.
* - Command-line arguments containing special characters are more accurately passed
* through to the child process.
* - If the filename is missing a path, then the shell's default PATH will be searched.
* - If the filename is missing a file extension, then Windows default file extensions
* will be searched.
*
* @param filename - The name of the executable file. This string must not contain any
* command-line arguments. If the name contains any path delimiters, then the shell's
* default PATH will not be searched.
* @param args - The command-line arguments to be passed to the process.
* @param options - Additional options
* @returns the same data type as returned by the NodeJS child_process.spawnSync() API
*
* @privateRemarks
*
* NOTE: The NodeJS spawnSync() returns SpawnSyncReturns<string> or SpawnSyncReturns<Buffer>
* polymorphically based on the options.encoding parameter value. This is a fairly confusing
* design. In most cases, developers want string with the default encoding. If/when someone
* wants binary output or a non-default text encoding, we will introduce a separate API function
* with a name like "spawnWithBufferSync".
*/
static spawnSync(filename: string, args: string[], options?: IExecutableSpawnSyncOptions): child_process.SpawnSyncReturns<string>;
/**
* Start a child process.
*
* @remarks
* This function is similar to child_process.spawn(). The main differences are:
*
* - It does not invoke the OS shell unless the executable file is a shell script.
* - Command-line arguments containing special characters are more accurately passed
* through to the child process.
* - If the filename is missing a path, then the shell's default PATH will be searched.
* - If the filename is missing a file extension, then Windows default file extensions
* will be searched.
*
* This command is asynchronous, but it does not return a `Promise`. Instead it returns
* a Node.js `ChildProcess` supporting event notifications.
*
* @param filename - The name of the executable file. This string must not contain any
* command-line arguments. If the name contains any path delimiters, then the shell's
* default PATH will not be searched.
* @param args - The command-line arguments to be passed to the process.
* @param options - Additional options
* @returns the same data type as returned by the NodeJS child_process.spawnSync() API
*/
static spawn(filename: string, args: string[], options?: IExecutableSpawnOptions): child_process.ChildProcess;
/** {@inheritDoc Executable.(waitForExitAsync:3)} */
static waitForExitAsync(childProcess: child_process.ChildProcess, options: IWaitForExitWithStringOptions): Promise<IWaitForExitResult<string>>;
/** {@inheritDoc Executable.(waitForExitAsync:3)} */
static waitForExitAsync(childProcess: child_process.ChildProcess, options: IWaitForExitWithBufferOptions): Promise<IWaitForExitResult<Buffer>>;
/**
* Wait for a child process to exit and return the result.
*
* @param childProcess - The child process to wait for.
* @param options - Options for waiting for the process to exit.
*/
static waitForExitAsync(childProcess: child_process.ChildProcess, options?: IWaitForExitOptions): Promise<IWaitForExitResult<never>>;
/**
* Get the list of processes currently running on the system, keyed by the process ID.
*
* @remarks The underlying implementation depends on the operating system:
* - On Windows, this uses the `wmic.exe` utility.
* - On Unix, this uses the `ps` utility.
*/
static getProcessInfoByIdAsync(): Promise<Map<number, IProcessInfo>>;
/**
* {@inheritDoc Executable.getProcessInfoByIdAsync}
*/
static getProcessInfoById(): Map<number, IProcessInfo>;
/**
* Get the list of processes currently running on the system, keyed by the process name. All processes
* with the same name will be grouped.
*
* @remarks The underlying implementation depends on the operating system:
* - On Windows, this uses the `wmic.exe` utility.
* - On Unix, this uses the `ps` utility.
*/
static getProcessInfoByNameAsync(): Promise<Map<string, IProcessInfo[]>>;
/**
* {@inheritDoc Executable.getProcessInfoByNameAsync}
*/
static getProcessInfoByName(): Map<string, IProcessInfo[]>;
private static _buildCommandLineFixup;
/**
* Given a filename, this determines the absolute path of the executable file that would
* be executed by a shell:
*
* - If the filename is missing a path, then the shell's default PATH will be searched.
* - If the filename is missing a file extension, then Windows default file extensions
* will be searched.
*
* @remarks
*
* @param filename - The name of the executable file. This string must not contain any
* command-line arguments. If the name contains any path delimiters, then the shell's
* default PATH will not be searched.
* @param options - optional other parameters
* @returns the absolute path of the executable, or undefined if it was not found
*/
static tryResolve(filename: string, options?: IExecutableResolveOptions): string | undefined;
private static _tryResolve;
private static _tryResolveFileExtension;
private static _buildEnvironmentMap;
/**
* This is used when searching the shell PATH for an executable, to determine
* whether a match should be skipped or not. If it returns true, this does not
* guarantee that the file can be successfully executed.
*/
private static _canExecute;
/**
* Returns the list of folders where we will search for an executable,
* based on the PATH environment variable.
*/
private static _getSearchFolders;
private static _getExecutableContext;
/**
* Given an input string containing special symbol characters, this inserts the "^" escape
* character to ensure the symbols are interpreted literally by the Windows shell.
*/
private static _getEscapedForWindowsShell;
/**
* Checks for characters that are unsafe to pass to a Windows batch file
* due to the way that cmd.exe implements escaping.
*/
private static _validateArgsForWindowsShell;
}
//# sourceMappingURL=Executable.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"Executable.d.ts","sourceRoot":"","sources":["../src/Executable.ts"],"names":[],"mappings":"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"}
+698
View File
@@ -0,0 +1,698 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Executable = void 0;
exports.parseProcessListOutputAsync = parseProcessListOutputAsync;
exports.parseProcessListOutput = parseProcessListOutput;
const os = __importStar(require("os"));
const child_process = __importStar(require("child_process"));
const path = __importStar(require("path"));
const EnvironmentMap_1 = require("./EnvironmentMap");
const FileSystem_1 = require("./FileSystem");
const PosixModeBits_1 = require("./PosixModeBits");
const Text_1 = require("./Text");
const InternalError_1 = require("./InternalError");
const OS_PLATFORM = os.platform();
async function parseProcessListOutputAsync(stream, platform = OS_PLATFORM) {
var _a, e_1, _b, _c;
const processInfoById = new Map();
let seenHeaders = false;
try {
for (var _d = true, _e = __asyncValues(Text_1.Text.readLinesFromIterableAsync(stream, { ignoreEmptyLines: true })), _f; _f = await _e.next(), _a = _f.done, !_a; _d = true) {
_c = _f.value;
_d = false;
const line = _c;
if (!seenHeaders) {
seenHeaders = true;
}
else {
parseProcessInfoEntry(line, processInfoById, platform);
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
}
finally { if (e_1) throw e_1.error; }
}
return processInfoById;
}
function parseProcessListOutput(
// eslint-disable-next-line @rushstack/no-new-null
output, platform = OS_PLATFORM) {
const processInfoById = new Map();
let seenHeaders = false;
for (const line of Text_1.Text.readLinesFromIterable(output, { ignoreEmptyLines: true })) {
if (!seenHeaders) {
seenHeaders = true;
}
else {
parseProcessInfoEntry(line, processInfoById, platform);
}
}
return processInfoById;
}
// win32 format:
// Name ParentProcessId ProcessId
// process name 1234 5678
// unix format:
// PPID PID COMMAND
// 51234 56784 process name
const NAME_GROUP = 'name';
const PROCESS_ID_GROUP = 'pid';
const PARENT_PROCESS_ID_GROUP = 'ppid';
// eslint-disable-next-line @rushstack/security/no-unsafe-regexp
const PROCESS_LIST_ENTRY_REGEX_WIN32 = new RegExp(`^(?<${NAME_GROUP}>.+?)\\s+(?<${PARENT_PROCESS_ID_GROUP}>\\d+)\\s+(?<${PROCESS_ID_GROUP}>\\d+)\\s*$`);
// eslint-disable-next-line @rushstack/security/no-unsafe-regexp
const PROCESS_LIST_ENTRY_REGEX_UNIX = new RegExp(`^\\s*(?<${PARENT_PROCESS_ID_GROUP}>\\d+)\\s+(?<${PROCESS_ID_GROUP}>\\d+)\\s+(?<${NAME_GROUP}>.+?)\\s*$`);
function parseProcessInfoEntry(line, existingProcessInfoById, platform) {
const processListEntryRegex = platform === 'win32' ? PROCESS_LIST_ENTRY_REGEX_WIN32 : PROCESS_LIST_ENTRY_REGEX_UNIX;
const match = line.match(processListEntryRegex);
if (!(match === null || match === void 0 ? void 0 : match.groups)) {
throw new InternalError_1.InternalError(`Invalid process list entry: ${line}`);
}
const processName = match.groups[NAME_GROUP];
const processId = parseInt(match.groups[PROCESS_ID_GROUP], 10);
const parentProcessId = parseInt(match.groups[PARENT_PROCESS_ID_GROUP], 10);
// Only care about the parent process if it is not the same as the current process.
let parentProcessInfo;
if (parentProcessId !== processId) {
parentProcessInfo = existingProcessInfoById.get(parentProcessId);
if (!parentProcessInfo) {
// Create a new placeholder entry for the parent with the information we have so far
parentProcessInfo = {
processName: '',
processId: parentProcessId,
parentProcessInfo: undefined,
childProcessInfos: []
};
existingProcessInfoById.set(parentProcessId, parentProcessInfo);
}
}
let processInfo = existingProcessInfoById.get(processId);
if (!processInfo) {
// Create a new entry
processInfo = {
processName,
processId,
parentProcessInfo,
childProcessInfos: []
};
existingProcessInfoById.set(processId, processInfo);
}
else {
// Update placeholder entry
processInfo.processName = processName;
processInfo.parentProcessInfo = parentProcessInfo;
}
// Add the process as a child of the parent process
parentProcessInfo === null || parentProcessInfo === void 0 ? void 0 : parentProcessInfo.childProcessInfos.push(processInfo);
}
function convertToProcessInfoByNameMap(processInfoById) {
const processInfoByNameMap = new Map();
for (const processInfo of processInfoById.values()) {
let processInfoNameEntries = processInfoByNameMap.get(processInfo.processName);
if (!processInfoNameEntries) {
processInfoNameEntries = [];
processInfoByNameMap.set(processInfo.processName, processInfoNameEntries);
}
processInfoNameEntries.push(processInfo);
}
return processInfoByNameMap;
}
function getProcessListProcessOptions() {
let command;
let args;
if (OS_PLATFORM === 'win32') {
command = 'wmic.exe';
// Order of declared properties does not impact the order of the output
args = ['process', 'get', 'Name,ParentProcessId,ProcessId'];
}
else {
command = 'ps';
// -A: Select all processes
// -w: Wide format
// -o: User-defined format
// Order of declared properties impacts the order of the output. We will
// need to request the "comm" property last in order to ensure that the
// process names are not truncated on certain platforms
args = ['-Awo', 'ppid,pid,comm'];
}
return { path: command, args };
}
/**
* The Executable class provides a safe, portable, recommended solution for tools that need
* to launch child processes.
*
* @remarks
* The NodeJS child_process API provides a solution for launching child processes, however
* its design encourages reliance on the operating system shell for certain features.
* Invoking the OS shell is not safe, not portable, and generally not recommended:
*
* - Different shells have different behavior and command-line syntax, and which shell you
* will get with NodeJS is unpredictable. There is no universal shell guaranteed to be
* available on all platforms.
*
* - If a command parameter contains symbol characters, a shell may interpret them, which
* can introduce a security vulnerability
*
* - Each shell has different rules for escaping these symbols. On Windows, the default
* shell is incapable of escaping certain character sequences.
*
* The Executable API provides a pure JavaScript implementation of primitive shell-like
* functionality for searching the default PATH, appending default file extensions on Windows,
* and executing a file that may contain a POSIX shebang. This primitive functionality
* is sufficient (and recommended) for most tooling scenarios.
*
* If you need additional shell features such as wildcard globbing, environment variable
* expansion, piping, or built-in commands, then we recommend to use the `@microsoft/rushell`
* library instead. Rushell is a pure JavaScript shell with a standard syntax that is
* guaranteed to work consistently across all platforms.
*
* @public
*/
class Executable {
/**
* Synchronously create a child process and optionally capture its output.
*
* @remarks
* This function is similar to child_process.spawnSync(). The main differences are:
*
* - It does not invoke the OS shell unless the executable file is a shell script.
* - Command-line arguments containing special characters are more accurately passed
* through to the child process.
* - If the filename is missing a path, then the shell's default PATH will be searched.
* - If the filename is missing a file extension, then Windows default file extensions
* will be searched.
*
* @param filename - The name of the executable file. This string must not contain any
* command-line arguments. If the name contains any path delimiters, then the shell's
* default PATH will not be searched.
* @param args - The command-line arguments to be passed to the process.
* @param options - Additional options
* @returns the same data type as returned by the NodeJS child_process.spawnSync() API
*
* @privateRemarks
*
* NOTE: The NodeJS spawnSync() returns SpawnSyncReturns<string> or SpawnSyncReturns<Buffer>
* polymorphically based on the options.encoding parameter value. This is a fairly confusing
* design. In most cases, developers want string with the default encoding. If/when someone
* wants binary output or a non-default text encoding, we will introduce a separate API function
* with a name like "spawnWithBufferSync".
*/
static spawnSync(filename, args, options) {
if (!options) {
options = {};
}
const context = Executable._getExecutableContext(options);
const resolvedPath = Executable._tryResolve(filename, options, context);
if (!resolvedPath) {
throw new Error(`The executable file was not found: "${filename}"`);
}
const spawnOptions = {
cwd: context.currentWorkingDirectory,
env: context.environmentMap.toObject(),
input: options.input,
stdio: options.stdio,
timeout: options.timeoutMs,
maxBuffer: options.maxBuffer,
// Contrary to what the NodeJS typings imply, we must explicitly specify "utf8" here
// if we want the result to be SpawnSyncReturns<string> instead of SpawnSyncReturns<Buffer>.
encoding: 'utf8',
// NOTE: This is always false, because Rushell will be recommended instead of relying on the OS shell.
shell: false
};
const normalizedCommandLine = Executable._buildCommandLineFixup(resolvedPath, args, context);
return child_process.spawnSync(normalizedCommandLine.path, normalizedCommandLine.args, spawnOptions);
}
/**
* Start a child process.
*
* @remarks
* This function is similar to child_process.spawn(). The main differences are:
*
* - It does not invoke the OS shell unless the executable file is a shell script.
* - Command-line arguments containing special characters are more accurately passed
* through to the child process.
* - If the filename is missing a path, then the shell's default PATH will be searched.
* - If the filename is missing a file extension, then Windows default file extensions
* will be searched.
*
* This command is asynchronous, but it does not return a `Promise`. Instead it returns
* a Node.js `ChildProcess` supporting event notifications.
*
* @param filename - The name of the executable file. This string must not contain any
* command-line arguments. If the name contains any path delimiters, then the shell's
* default PATH will not be searched.
* @param args - The command-line arguments to be passed to the process.
* @param options - Additional options
* @returns the same data type as returned by the NodeJS child_process.spawnSync() API
*/
static spawn(filename, args, options) {
if (!options) {
options = {};
}
const context = Executable._getExecutableContext(options);
const resolvedPath = Executable._tryResolve(filename, options, context);
if (!resolvedPath) {
throw new Error(`The executable file was not found: "${filename}"`);
}
const spawnOptions = {
cwd: context.currentWorkingDirectory,
env: context.environmentMap.toObject(),
stdio: options.stdio,
// NOTE: This is always false, because Rushell will be recommended instead of relying on the OS shell.
shell: false
};
const normalizedCommandLine = Executable._buildCommandLineFixup(resolvedPath, args, context);
return child_process.spawn(normalizedCommandLine.path, normalizedCommandLine.args, spawnOptions);
}
static async waitForExitAsync(childProcess, options = {}) {
const { throwOnNonZeroExitCode, throwOnSignal, encoding } = options;
if (encoding && (!childProcess.stdout || !childProcess.stderr)) {
throw new Error('An encoding was specified, but stdout and/or stderr on the child process are not defined');
}
const collectedStdout = [];
const collectedStderr = [];
const useBufferEncoding = encoding === 'buffer';
function normalizeChunk(chunk) {
if (typeof chunk === 'string') {
return (useBufferEncoding ? Buffer.from(chunk) : chunk);
}
else {
return (useBufferEncoding ? chunk : chunk.toString(encoding));
}
}
let errorThrown = undefined;
const { exitCode, signal } = await new Promise((resolve, reject) => {
if (encoding) {
childProcess.stdout.on('data', (chunk) => {
collectedStdout.push(normalizeChunk(chunk));
});
childProcess.stderr.on('data', (chunk) => {
collectedStderr.push(normalizeChunk(chunk));
});
}
childProcess.on('error', (error) => {
// Wait to call reject() until any output is collected
errorThrown = error;
});
childProcess.on('close', (closeExitCode, closeSignal) => {
if (errorThrown) {
reject(errorThrown);
}
if (closeSignal && throwOnSignal) {
reject(new Error(`Process terminated by ${closeSignal}`));
}
else if (closeExitCode !== 0 && throwOnNonZeroExitCode) {
reject(new Error(`Process exited with code ${closeExitCode}`));
}
else {
resolve({ exitCode: closeExitCode, signal: closeSignal });
}
});
});
let stdout;
let stderr;
if (encoding === 'buffer') {
stdout = Buffer.concat(collectedStdout);
stderr = Buffer.concat(collectedStderr);
}
else if (encoding !== undefined) {
stdout = collectedStdout.join('');
stderr = collectedStderr.join('');
}
const result = {
stdout: stdout,
stderr: stderr,
exitCode,
signal
};
return result;
}
/* eslint-enable @rushstack/no-new-null */
/**
* Get the list of processes currently running on the system, keyed by the process ID.
*
* @remarks The underlying implementation depends on the operating system:
* - On Windows, this uses the `wmic.exe` utility.
* - On Unix, this uses the `ps` utility.
*/
static async getProcessInfoByIdAsync() {
const { path: command, args } = getProcessListProcessOptions();
const process = Executable.spawn(command, args, {
stdio: ['ignore', 'pipe', 'ignore']
});
if (process.stdout === null) {
throw new InternalError_1.InternalError('Child process did not provide stdout');
}
const [processInfoByIdMap] = await Promise.all([
parseProcessListOutputAsync(process.stdout),
// Don't collect output in the result since we process it directly
Executable.waitForExitAsync(process, { throwOnNonZeroExitCode: true, throwOnSignal: true })
]);
return processInfoByIdMap;
}
/**
* {@inheritDoc Executable.getProcessInfoByIdAsync}
*/
static getProcessInfoById() {
const { path: command, args } = getProcessListProcessOptions();
const processOutput = Executable.spawnSync(command, args);
if (processOutput.error) {
throw new Error(`Unable to list processes: ${command} failed with error ${processOutput.error}`);
}
if (processOutput.status !== 0) {
throw new Error(`Unable to list processes: ${command} exited with code ${processOutput.status}`);
}
return parseProcessListOutput(processOutput.output);
}
/**
* Get the list of processes currently running on the system, keyed by the process name. All processes
* with the same name will be grouped.
*
* @remarks The underlying implementation depends on the operating system:
* - On Windows, this uses the `wmic.exe` utility.
* - On Unix, this uses the `ps` utility.
*/
static async getProcessInfoByNameAsync() {
const processInfoById = await Executable.getProcessInfoByIdAsync();
return convertToProcessInfoByNameMap(processInfoById);
}
/**
* {@inheritDoc Executable.getProcessInfoByNameAsync}
*/
static getProcessInfoByName() {
const processInfoByIdMap = Executable.getProcessInfoById();
return convertToProcessInfoByNameMap(processInfoByIdMap);
}
// PROBLEM: Given an "args" array of strings that may contain special characters (e.g. spaces,
// backslashes, quotes), ensure that these strings pass through to the child process's ARGV array
// without anything getting corrupted along the way.
//
// On Unix you just pass the array to spawnSync(). But on Windows, this is a very complex problem:
// - The Win32 CreateProcess() API expects the args to be encoded as a single text string
// - The decoding of this string is up to the application (not the OS), and there are 3 different
// algorithms in common usage: the cmd.exe shell, the Microsoft CRT library init code, and
// the Win32 CommandLineToArgvW()
// - The encodings are counterintuitive and have lots of special cases
// - NodeJS spawnSync() tries do the encoding without knowing which decoder will be used
//
// See these articles for a full analysis:
// http://www.windowsinspired.com/understanding-the-command-line-string-and-arguments-received-by-a-windows-program/
// http://www.windowsinspired.com/how-a-windows-programs-splits-its-command-line-into-individual-arguments/
static _buildCommandLineFixup(resolvedPath, args, context) {
const fileExtension = path.extname(resolvedPath);
if (OS_PLATFORM === 'win32') {
// Do we need a custom handler for this file type?
switch (fileExtension.toUpperCase()) {
case '.EXE':
case '.COM':
// okay to execute directly
break;
case '.BAT':
case '.CMD': {
Executable._validateArgsForWindowsShell(args);
// These file types must be invoked via the Windows shell
let shellPath = context.environmentMap.get('COMSPEC');
if (!shellPath || !Executable._canExecute(shellPath, context)) {
shellPath = Executable.tryResolve('cmd.exe');
}
if (!shellPath) {
throw new Error(`Unable to execute "${path.basename(resolvedPath)}" ` +
`because CMD.exe was not found in the PATH`);
}
const shellArgs = [];
// /D: Disable execution of AutoRun commands when starting the new shell context
shellArgs.push('/d');
// /S: Disable Cmd.exe's parsing of double-quote characters inside the command-line
shellArgs.push('/s');
// /C: Execute the following command and then exit immediately
shellArgs.push('/c');
// If the path contains special charactrers (e.g. spaces), escape them so that
// they don't get interpreted by the shell
shellArgs.push(Executable._getEscapedForWindowsShell(resolvedPath));
shellArgs.push(...args);
return { path: shellPath, args: shellArgs };
}
default:
throw new Error(`Cannot execute "${path.basename(resolvedPath)}" because the file type is not supported`);
}
}
return {
path: resolvedPath,
args: args
};
}
/**
* Given a filename, this determines the absolute path of the executable file that would
* be executed by a shell:
*
* - If the filename is missing a path, then the shell's default PATH will be searched.
* - If the filename is missing a file extension, then Windows default file extensions
* will be searched.
*
* @remarks
*
* @param filename - The name of the executable file. This string must not contain any
* command-line arguments. If the name contains any path delimiters, then the shell's
* default PATH will not be searched.
* @param options - optional other parameters
* @returns the absolute path of the executable, or undefined if it was not found
*/
static tryResolve(filename, options) {
return Executable._tryResolve(filename, options || {}, Executable._getExecutableContext(options));
}
static _tryResolve(filename, options, context) {
// NOTE: Since "filename" cannot contain command-line arguments, the "/" here
// must be interpreted as a path delimiter
const hasPathSeparators = filename.indexOf('/') >= 0 || (OS_PLATFORM === 'win32' && filename.indexOf('\\') >= 0);
// Are there any path separators?
if (hasPathSeparators) {
// If so, then don't search the PATH. Just resolve relative to the current working directory
const resolvedPath = path.resolve(context.currentWorkingDirectory, filename);
return Executable._tryResolveFileExtension(resolvedPath, context);
}
else {
// Otherwise if it's a bare name, then try everything in the shell PATH
const pathsToSearch = Executable._getSearchFolders(context);
for (const pathToSearch of pathsToSearch) {
const resolvedPath = path.join(pathToSearch, filename);
const result = Executable._tryResolveFileExtension(resolvedPath, context);
if (result) {
return result;
}
}
// No match was found
return undefined;
}
}
static _tryResolveFileExtension(resolvedPath, context) {
if (Executable._canExecute(resolvedPath, context)) {
return resolvedPath;
}
// Try the default file extensions
for (const shellExtension of context.windowsExecutableExtensions) {
const resolvedNameWithExtension = resolvedPath + shellExtension;
if (Executable._canExecute(resolvedNameWithExtension, context)) {
return resolvedNameWithExtension;
}
}
return undefined;
}
static _buildEnvironmentMap(options) {
const environmentMap = new EnvironmentMap_1.EnvironmentMap();
if (options.environment !== undefined && options.environmentMap !== undefined) {
throw new Error('IExecutableResolveOptions.environment and IExecutableResolveOptions.environmentMap' +
' cannot both be specified');
}
if (options.environment !== undefined) {
environmentMap.mergeFromObject(options.environment);
}
else if (options.environmentMap !== undefined) {
environmentMap.mergeFrom(options.environmentMap);
}
else {
environmentMap.mergeFromObject(process.env);
}
return environmentMap;
}
/**
* This is used when searching the shell PATH for an executable, to determine
* whether a match should be skipped or not. If it returns true, this does not
* guarantee that the file can be successfully executed.
*/
static _canExecute(filePath, context) {
if (!FileSystem_1.FileSystem.exists(filePath)) {
return false;
}
if (OS_PLATFORM === 'win32') {
// NOTE: For Windows, we don't validate that the file extension appears in PATHEXT.
// That environment variable determines which extensions can be appended if the
// extension is missing, but it does not affect whether a file may be executed or not.
// Windows does have a (seldom used) ACL that can be used to deny execution permissions
// for a file, but NodeJS doesn't expose that API, so we don't bother checking it.
// However, Windows *does* require that the file has some kind of file extension
if (path.extname(filePath) === '') {
return false;
}
}
else {
// For Unix, check whether any of the POSIX execute bits are set
try {
// eslint-disable-next-line no-bitwise
if ((FileSystem_1.FileSystem.getPosixModeBits(filePath) & PosixModeBits_1.PosixModeBits.AllExecute) === 0) {
return false; // not executable
}
}
catch (error) {
// If we have trouble accessing the file, ignore the error and consider it "not executable"
// since that's what a shell would do
}
}
return true;
}
/**
* Returns the list of folders where we will search for an executable,
* based on the PATH environment variable.
*/
static _getSearchFolders(context) {
const pathList = context.environmentMap.get('PATH') || '';
const folders = [];
// Avoid processing duplicates
const seenPaths = new Set();
// NOTE: Cmd.exe on Windows always searches the current working directory first.
// PowerShell and Unix shells do NOT do that, because it's a security concern.
// We follow their behavior.
for (const splitPath of pathList.split(path.delimiter)) {
const trimmedPath = splitPath.trim();
if (trimmedPath !== '') {
if (!seenPaths.has(trimmedPath)) {
// Fun fact: If you put relative paths in your PATH environment variable,
// all shells will dynamically match them against the current working directory.
// This is a terrible design, and in practice nobody does that, but it is supported...
// so we allow it here.
const resolvedPath = path.resolve(context.currentWorkingDirectory, trimmedPath);
if (!seenPaths.has(resolvedPath)) {
if (FileSystem_1.FileSystem.exists(resolvedPath)) {
folders.push(resolvedPath);
}
seenPaths.add(resolvedPath);
}
seenPaths.add(trimmedPath);
}
}
}
return folders;
}
static _getExecutableContext(options) {
if (!options) {
options = {};
}
const environment = Executable._buildEnvironmentMap(options);
let currentWorkingDirectory;
if (options.currentWorkingDirectory) {
currentWorkingDirectory = path.resolve(options.currentWorkingDirectory);
}
else {
currentWorkingDirectory = process.cwd();
}
const windowsExecutableExtensions = [];
if (OS_PLATFORM === 'win32') {
const pathExtVariable = environment.get('PATHEXT') || '';
for (const splitValue of pathExtVariable.split(';')) {
const trimmed = splitValue.trim().toLowerCase();
// Ignore malformed extensions
if (/^\.[a-z0-9\.]*[a-z0-9]$/i.test(trimmed)) {
// Don't add the same extension twice
if (windowsExecutableExtensions.indexOf(trimmed) < 0) {
windowsExecutableExtensions.push(trimmed);
}
}
}
}
return {
environmentMap: environment,
currentWorkingDirectory,
windowsExecutableExtensions
};
}
/**
* Given an input string containing special symbol characters, this inserts the "^" escape
* character to ensure the symbols are interpreted literally by the Windows shell.
*/
static _getEscapedForWindowsShell(text) {
const escapableCharRegExp = /[%\^&|<> ]/g;
return text.replace(escapableCharRegExp, (value) => '^' + value);
}
/**
* Checks for characters that are unsafe to pass to a Windows batch file
* due to the way that cmd.exe implements escaping.
*/
static _validateArgsForWindowsShell(args) {
const specialCharRegExp = /[%\^&|<>\r\n]/g;
for (const arg of args) {
const match = arg.match(specialCharRegExp);
if (match) {
// NOTE: It is possible to escape some of these characters by prefixing them
// with a caret (^), which allows these characters to be successfully passed
// through to the batch file %1 variables. But they will be expanded again
// whenever they are used. For example, NPM's binary wrapper batch files
// use "%*" to pass their arguments to Node.exe, which causes them to be expanded
// again. Unfortunately the Cmd.exe batch language provides native escaping
// function (that could be used to insert the carets again).
//
// We could work around that by adding double carets, but in general there
// is no way to predict how many times the variable will get expanded.
// Thus, there is no generally reliable way to pass these characters.
throw new Error(`The command line argument ${JSON.stringify(arg)} contains a` +
` special character ${JSON.stringify(match[0])} that cannot be escaped for the Windows shell`);
}
}
}
}
exports.Executable = Executable;
//# sourceMappingURL=Executable.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Executable.js","sourceRoot":"","sources":["../src/Executable.ts"],"names":[],"mappings":"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 truncated
+81
View File
@@ -0,0 +1,81 @@
import { type FileLocationStyle } from './Path';
/**
* Provides options for the creation of a FileError.
*
* @public
*/
export interface IFileErrorOptions {
/**
* The absolute path to the file that contains the error.
*/
absolutePath: string;
/**
* The root folder for the project that the error is in relation to.
*/
projectFolder: string;
/**
* The line number of the error in the target file. Minimum value is 1.
*/
line?: number;
/**
* The column number of the error in the target file. Minimum value is 1.
*/
column?: number;
}
/**
* Provides options for the output message of a file error.
*
* @public
*/
export interface IFileErrorFormattingOptions {
/**
* The format for the error message. If no format is provided, format 'Unix' is used by default.
*/
format?: FileLocationStyle;
}
/**
* An `Error` subclass that should be thrown to report an unexpected state that specifically references
* a location in a file.
*
* @remarks The file path provided to the FileError constructor is expected to exist on disk. FileError
* should not be used for reporting errors that are not in reference to an existing file.
*
* @public
*/
export declare class FileError extends Error {
/** @internal */
static _sanitizedEnvironmentVariable: string | undefined;
/** @internal */
static _environmentVariableIsAbsolutePath: boolean;
private static _environmentVariableBasePathFnMap;
/** {@inheritdoc IFileErrorOptions.absolutePath} */
readonly absolutePath: string;
/** {@inheritdoc IFileErrorOptions.projectFolder} */
readonly projectFolder: string;
/** {@inheritdoc IFileErrorOptions.line} */
readonly line: number | undefined;
/** {@inheritdoc IFileErrorOptions.column} */
readonly column: number | undefined;
/**
* Constructs a new instance of the {@link FileError} class.
*
* @param message - A message describing the error.
* @param options - Options for the error.
*/
constructor(message: string, options: IFileErrorOptions);
/**
* Get the Unix-formatted the error message.
*
* @override
*/
toString(): string;
/**
* Get the formatted error message.
*
* @param options - Options for the error message format.
*/
getFormattedErrorMessage(options?: IFileErrorFormattingOptions): string;
private _evaluateBaseFolder;
static [Symbol.hasInstance](instance: object): boolean;
}
//# sourceMappingURL=FileError.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"FileError.d.ts","sourceRoot":"","sources":["../src/FileError.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,iBAAiB,EAAQ,MAAM,QAAQ,CAAC;AAGtD;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAMD;;;;;;;;GAQG;AACH,qBAAa,SAAU,SAAQ,KAAK;IAClC,gBAAgB;IAChB,OAAc,6BAA6B,EAAE,MAAM,GAAG,SAAS,CAAC;IAChE,gBAAgB;IAChB,OAAc,kCAAkC,EAAE,OAAO,CAAS;IAElE,OAAO,CAAC,MAAM,CAAC,iCAAiC,CAO7C;IAEH,mDAAmD;IACnD,SAAgB,YAAY,EAAE,MAAM,CAAC;IACrC,oDAAoD;IACpD,SAAgB,aAAa,EAAE,MAAM,CAAC;IACtC,2CAA2C;IAC3C,SAAgB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,6CAA6C;IAC7C,SAAgB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAE3C;;;;;OAKG;gBACgB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB;IAe9D;;;;OAIG;IACI,QAAQ,IAAI,MAAM;IAKzB;;;;OAIG;IACI,wBAAwB,CAAC,OAAO,CAAC,EAAE,2BAA2B,GAAG,MAAM;IAW9E,OAAO,CAAC,mBAAmB;WAgDb,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAG9D"}
+113
View File
@@ -0,0 +1,113 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileError = void 0;
const Path_1 = require("./Path");
const TypeUuid_1 = require("./TypeUuid");
const uuidFileError = '37a4c772-2dc8-4c66-89ae-262f8cc1f0c1';
const baseFolderEnvVar = 'RUSHSTACK_FILE_ERROR_BASE_FOLDER';
/**
* An `Error` subclass that should be thrown to report an unexpected state that specifically references
* a location in a file.
*
* @remarks The file path provided to the FileError constructor is expected to exist on disk. FileError
* should not be used for reporting errors that are not in reference to an existing file.
*
* @public
*/
class FileError extends Error {
/**
* Constructs a new instance of the {@link FileError} class.
*
* @param message - A message describing the error.
* @param options - Options for the error.
*/
constructor(message, options) {
super(message);
this.absolutePath = options.absolutePath;
this.projectFolder = options.projectFolder;
this.line = options.line;
this.column = options.column;
// Manually set the prototype, as we can no longer extend built-in classes like Error, Array, Map, etc.
// https://github.com/microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
//
// Note: the prototype must also be set on any classes which extend this one
this.__proto__ = FileError.prototype; // eslint-disable-line @typescript-eslint/no-explicit-any
}
/**
* Get the Unix-formatted the error message.
*
* @override
*/
toString() {
// Default to formatting in 'Unix' format, for consistency.
return this.getFormattedErrorMessage();
}
/**
* Get the formatted error message.
*
* @param options - Options for the error message format.
*/
getFormattedErrorMessage(options) {
return Path_1.Path.formatFileLocation({
format: (options === null || options === void 0 ? void 0 : options.format) || 'Unix',
baseFolder: this._evaluateBaseFolder(),
pathToFormat: this.absolutePath,
message: this.message,
line: this.line,
column: this.column
});
}
_evaluateBaseFolder() {
// Cache the sanitized environment variable. This means that we don't support changing
// the environment variable mid-execution. This is a reasonable tradeoff for the benefit
// of being able to cache absolute paths, since that is only able to be determined after
// running the regex, which is expensive. Since this would be a common execution path for
// tools like Rush, we should optimize for that.
if (!FileError._sanitizedEnvironmentVariable && process.env[baseFolderEnvVar]) {
// Strip leading and trailing quotes, if present.
FileError._sanitizedEnvironmentVariable = process.env[baseFolderEnvVar].replace(/^("|')|("|')$/g, '');
}
if (FileError._environmentVariableIsAbsolutePath) {
return FileError._sanitizedEnvironmentVariable;
}
// undefined environment variable has a mapping to the project folder
const baseFolderFn = FileError._environmentVariableBasePathFnMap.get(FileError._sanitizedEnvironmentVariable);
if (baseFolderFn) {
return baseFolderFn(this);
}
const baseFolderTokenRegex = /{([^}]+)}/g;
const result = baseFolderTokenRegex.exec(FileError._sanitizedEnvironmentVariable);
if (!result) {
// No tokens, assume absolute path
FileError._environmentVariableIsAbsolutePath = true;
return FileError._sanitizedEnvironmentVariable;
}
else if (result.index !== 0) {
// Currently only support the token being first in the string.
throw new Error(`The ${baseFolderEnvVar} environment variable contains text before the token "${result[0]}".`);
}
else if (result[0].length !== FileError._sanitizedEnvironmentVariable.length) {
// Currently only support the token being the entire string.
throw new Error(`The ${baseFolderEnvVar} environment variable contains text after the token "${result[0]}".`);
}
else {
throw new Error(`The ${baseFolderEnvVar} environment variable contains a token "${result[0]}", which is not ` +
'supported.');
}
}
static [Symbol.hasInstance](instance) {
return TypeUuid_1.TypeUuid.isInstanceOf(instance, uuidFileError);
}
}
exports.FileError = FileError;
/** @internal */
FileError._environmentVariableIsAbsolutePath = false;
FileError._environmentVariableBasePathFnMap = new Map([
[undefined, (fileError) => fileError.projectFolder],
['{PROJECT_FOLDER}', (fileError) => fileError.projectFolder],
['{ABSOLUTE_PATH}', (fileError) => undefined]
]);
TypeUuid_1.TypeUuid.registerClass(FileError, uuidFileError);
//# sourceMappingURL=FileError.js.map
@@ -0,0 +1 @@
{"version":3,"file":"FileError.js","sourceRoot":"","sources":["../src/FileError.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { type FileLocationStyle, Path } from './Path';\nimport { TypeUuid } from './TypeUuid';\n\n/**\n * Provides options for the creation of a FileError.\n *\n * @public\n */\nexport interface IFileErrorOptions {\n /**\n * The absolute path to the file that contains the error.\n */\n absolutePath: string;\n\n /**\n * The root folder for the project that the error is in relation to.\n */\n projectFolder: string;\n\n /**\n * The line number of the error in the target file. Minimum value is 1.\n */\n line?: number;\n\n /**\n * The column number of the error in the target file. Minimum value is 1.\n */\n column?: number;\n}\n\n/**\n * Provides options for the output message of a file error.\n *\n * @public\n */\nexport interface IFileErrorFormattingOptions {\n /**\n * The format for the error message. If no format is provided, format 'Unix' is used by default.\n */\n format?: FileLocationStyle;\n}\n\nconst uuidFileError: string = '37a4c772-2dc8-4c66-89ae-262f8cc1f0c1';\n\nconst baseFolderEnvVar: string = 'RUSHSTACK_FILE_ERROR_BASE_FOLDER';\n\n/**\n * An `Error` subclass that should be thrown to report an unexpected state that specifically references\n * a location in a file.\n *\n * @remarks The file path provided to the FileError constructor is expected to exist on disk. FileError\n * should not be used for reporting errors that are not in reference to an existing file.\n *\n * @public\n */\nexport class FileError extends Error {\n /** @internal */\n public static _sanitizedEnvironmentVariable: string | undefined;\n /** @internal */\n public static _environmentVariableIsAbsolutePath: boolean = false;\n\n private static _environmentVariableBasePathFnMap: ReadonlyMap<\n string | undefined,\n (fileError: FileError) => string | undefined\n > = new Map([\n [undefined, (fileError: FileError) => fileError.projectFolder],\n ['{PROJECT_FOLDER}', (fileError: FileError) => fileError.projectFolder],\n ['{ABSOLUTE_PATH}', (fileError: FileError) => undefined as string | undefined]\n ]);\n\n /** {@inheritdoLine truncated
+687
View File
@@ -0,0 +1,687 @@
import * as fs from 'fs';
import { type NewlineKind, Encoding } from './Text';
import { PosixModeBits } from './PosixModeBits';
/**
* An alias for the Node.js `fs.Stats` object.
*
* @remarks
* This avoids the need to import the `fs` package when using the {@link FileSystem} API.
* @public
*/
export type FileSystemStats = fs.Stats;
/**
* An alias for the Node.js `fs.Dirent` object.
*
* @remarks
* This avoids the need to import the `fs` package when using the {@link FileSystem} API.
* @public
*/
export type FolderItem = fs.Dirent;
/**
* The options for {@link FileSystem.readFolderItems} and {@link FileSystem.readFolderItemNames}.
* @public
*/
export interface IFileSystemReadFolderOptions {
/**
* If true, returns the absolute paths of the files in the folder.
* @defaultValue false
*/
absolutePaths?: boolean;
}
/**
* The options for {@link FileSystem.writeBuffersToFile}
* @public
*/
export interface IFileSystemWriteBinaryFileOptions {
/**
* If true, will ensure the folder is created before writing the file.
* @defaultValue false
*/
ensureFolderExists?: boolean;
}
/**
* The options for {@link FileSystem.writeFile}
* @public
*/
export interface IFileSystemWriteFileOptions extends IFileSystemWriteBinaryFileOptions {
/**
* If specified, will normalize line endings to the specified style of newline.
* @defaultValue `undefined` which means no conversion will be performed
*/
convertLineEndings?: NewlineKind;
/**
* If specified, will change the encoding of the file that will be written.
* @defaultValue "utf8"
*/
encoding?: Encoding;
}
/**
* The options for {@link FileSystem.readFile}
* @public
*/
export interface IFileSystemReadFileOptions {
/**
* If specified, will change the encoding of the file that will be written.
* @defaultValue Encoding.Utf8
*/
encoding?: Encoding;
/**
* If specified, will normalize line endings to the specified style of newline.
* @defaultValue `undefined` which means no conversion will be performed
*/
convertLineEndings?: NewlineKind;
}
/**
* The options for {@link FileSystem.move}
* @public
*/
export interface IFileSystemMoveOptions {
/**
* The path of the existing object to be moved.
* The path may be absolute or relative.
*/
sourcePath: string;
/**
* The new path for the object.
* The path may be absolute or relative.
*/
destinationPath: string;
/**
* If true, will overwrite the file if it already exists.
* @defaultValue true
*/
overwrite?: boolean;
/**
* If true, will ensure the folder is created before writing the file.
* @defaultValue false
*/
ensureFolderExists?: boolean;
}
/**
* @public
*/
export interface IFileSystemCopyFileBaseOptions {
/**
* The path of the existing object to be copied.
* The path may be absolute or relative.
*/
sourcePath: string;
/**
* Specifies what to do if the destination path already exists.
* @defaultValue {@link AlreadyExistsBehavior.Overwrite}
*/
alreadyExistsBehavior?: AlreadyExistsBehavior;
}
/**
* The options for {@link FileSystem.copyFile}
* @public
*/
export interface IFileSystemCopyFileOptions extends IFileSystemCopyFileBaseOptions {
/**
* The path that the object will be copied to.
* The path may be absolute or relative.
*/
destinationPath: string;
}
/**
* Specifies the behavior of APIs such as {@link FileSystem.copyFile} or
* {@link FileSystem.createSymbolicLinkFile} when the output file path already exists.
*
* @remarks
* For {@link FileSystem.copyFile} and related APIs, the "output file path" is
* {@link IFileSystemCopyFileOptions.destinationPath}.
*
* For {@link FileSystem.createSymbolicLinkFile} and related APIs, the "output file path" is
* {@link IFileSystemCreateLinkOptions.newLinkPath}.
*
* @public
*/
export declare enum AlreadyExistsBehavior {
/**
* If the output file path already exists, try to overwrite the existing object.
*
* @remarks
* If overwriting the object would require recursively deleting a folder tree,
* then the operation will fail. As an example, suppose {@link FileSystem.copyFile}
* is copying a single file `/a/b/c` to the destination path `/d/e`, and `/d/e` is a
* nonempty folder. In this situation, an error will be reported; specifying
* `AlreadyExistsBehavior.Overwrite` does not help. Empty folders can be overwritten
* depending on the details of the implementation.
*/
Overwrite = "overwrite",
/**
* If the output file path already exists, the operation will fail, and an error
* will be reported.
*/
Error = "error",
/**
* If the output file path already exists, skip this item, and continue the operation.
*/
Ignore = "ignore"
}
/**
* Callback function type for {@link IFileSystemCopyFilesAsyncOptions.filter}
* @public
*/
export type FileSystemCopyFilesAsyncFilter = (sourcePath: string, destinationPath: string) => Promise<boolean>;
/**
* Callback function type for {@link IFileSystemCopyFilesOptions.filter}
* @public
*/
export type FileSystemCopyFilesFilter = (sourcePath: string, destinationPath: string) => boolean;
/**
* The options for {@link FileSystem.copyFilesAsync}
* @public
*/
export interface IFileSystemCopyFilesAsyncOptions {
/**
* The starting path of the file or folder to be copied.
* The path may be absolute or relative.
*/
sourcePath: string;
/**
* The path that the files will be copied to.
* The path may be absolute or relative.
*/
destinationPath: string;
/**
* If true, then when copying symlinks, copy the target object instead of copying the link.
*/
dereferenceSymlinks?: boolean;
/**
* Specifies what to do if a destination path already exists.
*
* @remarks
* This setting is applied individually for each file being copied.
* For example, `AlreadyExistsBehavior.Overwrite` will not recursively delete a folder
* whose path corresponds to an individual file that is being copied to that location.
*/
alreadyExistsBehavior?: AlreadyExistsBehavior;
/**
* If true, then the target object will be assigned "last modification" and "last access" timestamps
* that are the same as the source. Otherwise, the OS default timestamps are assigned.
*/
preserveTimestamps?: boolean;
/**
* A callback that will be invoked for each path that is copied. The callback can return `false`
* to cause the object to be excluded from the operation.
*/
filter?: FileSystemCopyFilesAsyncFilter | FileSystemCopyFilesFilter;
}
/**
* The options for {@link FileSystem.copyFiles}
* @public
*/
export interface IFileSystemCopyFilesOptions extends IFileSystemCopyFilesAsyncOptions {
/** {@inheritdoc IFileSystemCopyFilesAsyncOptions.filter} */
filter?: FileSystemCopyFilesFilter;
}
/**
* The options for {@link FileSystem.deleteFile}
* @public
*/
export interface IFileSystemDeleteFileOptions {
/**
* If true, will throw an exception if the file did not exist before `deleteFile()` was called.
* @defaultValue false
*/
throwIfNotExists?: boolean;
}
/**
* The options for {@link FileSystem.updateTimes}
* Both times must be specified.
* @public
*/
export interface IFileSystemUpdateTimeParameters {
/**
* The POSIX epoch time or Date when this was last accessed.
*/
accessedTime: number | Date;
/**
* The POSIX epoch time or Date when this was last modified
*/
modifiedTime: number | Date;
}
/**
* The options for {@link FileSystem.createSymbolicLinkJunction}, {@link FileSystem.createSymbolicLinkFile},
* {@link FileSystem.createSymbolicLinkFolder}, and {@link FileSystem.createHardLink}.
*
* @public
*/
export interface IFileSystemCreateLinkOptions {
/**
* The newly created symbolic link will point to `linkTargetPath` as its target.
*/
linkTargetPath: string;
/**
* The newly created symbolic link will have this path.
*/
newLinkPath: string;
/**
* Specifies what to do if the path to create already exists.
* The default is `AlreadyExistsBehavior.Error`.
*/
alreadyExistsBehavior?: AlreadyExistsBehavior;
}
/**
* The FileSystem API provides a complete set of recommended operations for interacting with the file system.
*
* @remarks
* We recommend to use this instead of the native `fs` API, because `fs` is a minimal set of low-level
* primitives that must be mapped for each supported operating system. The FileSystem API takes a
* philosophical approach of providing "one obvious way" to do each operation. We also prefer synchronous
* operations except in cases where there would be a clear performance benefit for using async, since synchronous
* code is much easier to read and debug. Also, indiscriminate parallelism has been seen to actually worsen
* performance, versus improving it.
*
* Note that in the documentation, we refer to "filesystem objects", this can be a
* file, folder, symbolic link, hard link, directory junction, etc.
*
* @public
*/
export declare class FileSystem {
/**
* Returns true if the path exists on disk.
* Behind the scenes it uses `fs.existsSync()`.
* @remarks
* There is a debate about the fact that after `fs.existsSync()` returns true,
* the file might be deleted before fs.readSync() is called, which would imply that everybody
* should catch a `readSync()` exception, and nobody should ever use `fs.existsSync()`.
* We find this to be unpersuasive, since "unexceptional exceptions" really hinder the
* break-on-exception debugging experience. Also, throwing/catching is generally slow.
* @param path - The absolute or relative path to the filesystem object.
*/
static exists(path: string): boolean;
/**
* An async version of {@link FileSystem.exists}.
*/
static existsAsync(path: string): Promise<boolean>;
/**
* Gets the statistics for a particular filesystem object.
* If the path is a link, this function follows the link and returns statistics about the link target.
* Behind the scenes it uses `fs.statSync()`.
* @param path - The absolute or relative path to the filesystem object.
*/
static getStatistics(path: string): FileSystemStats;
/**
* An async version of {@link FileSystem.getStatistics}.
*/
static getStatisticsAsync(path: string): Promise<FileSystemStats>;
/**
* Updates the accessed and modified timestamps of the filesystem object referenced by path.
* Behind the scenes it uses `fs.utimesSync()`.
* The caller should specify both times in the `times` parameter.
* @param path - The path of the file that should be modified.
* @param times - The times that the object should be updated to reflect.
*/
static updateTimes(path: string, times: IFileSystemUpdateTimeParameters): void;
/**
* An async version of {@link FileSystem.updateTimes}.
*/
static updateTimesAsync(path: string, times: IFileSystemUpdateTimeParameters): Promise<void>;
/**
* Changes the permissions (i.e. file mode bits) for a filesystem object.
* Behind the scenes it uses `fs.chmodSync()`.
* @param path - The absolute or relative path to the object that should be updated.
* @param modeBits - POSIX-style file mode bits specified using the {@link PosixModeBits} enum
*/
static changePosixModeBits(path: string, modeBits: PosixModeBits): void;
/**
* An async version of {@link FileSystem.changePosixModeBits}.
*/
static changePosixModeBitsAsync(path: string, mode: PosixModeBits): Promise<void>;
/**
* Retrieves the permissions (i.e. file mode bits) for a filesystem object.
* Behind the scenes it uses `fs.chmodSync()`.
* @param path - The absolute or relative path to the object that should be updated.
*
* @remarks
* This calls {@link FileSystem.getStatistics} to get the POSIX mode bits.
* If statistics in addition to the mode bits are needed, it is more efficient
* to call {@link FileSystem.getStatistics} directly instead.
*/
static getPosixModeBits(path: string): PosixModeBits;
/**
* An async version of {@link FileSystem.getPosixModeBits}.
*/
static getPosixModeBitsAsync(path: string): Promise<PosixModeBits>;
/**
* Returns a 10-character string representation of a PosixModeBits value similar to what
* would be displayed by a command such as "ls -l" on a POSIX-like operating system.
* @remarks
* For example, `PosixModeBits.AllRead | PosixModeBits.AllWrite` would be formatted as "-rw-rw-rw-".
* @param modeBits - POSIX-style file mode bits specified using the {@link PosixModeBits} enum
*/
static formatPosixModeBits(modeBits: PosixModeBits): string;
/**
* Moves a file. The folder must exist, unless the `ensureFolderExists` option is provided.
* Behind the scenes it uses `fs-extra.moveSync()`
*/
static move(options: IFileSystemMoveOptions): void;
/**
* An async version of {@link FileSystem.move}.
*/
static moveAsync(options: IFileSystemMoveOptions): Promise<void>;
/**
* Recursively creates a folder at a given path.
* Behind the scenes is uses `fs-extra.ensureDirSync()`.
* @remarks
* Throws an exception if anything in the folderPath is not a folder.
* @param folderPath - The absolute or relative path of the folder which should be created.
*/
static ensureFolder(folderPath: string): void;
/**
* An async version of {@link FileSystem.ensureFolder}.
*/
static ensureFolderAsync(folderPath: string): Promise<void>;
/**
* Reads the names of folder entries, not including "." or "..".
* Behind the scenes it uses `fs.readdirSync()`.
* @param folderPath - The absolute or relative path to the folder which should be read.
* @param options - Optional settings that can change the behavior. Type: `IReadFolderOptions`
*/
static readFolderItemNames(folderPath: string, options?: IFileSystemReadFolderOptions): string[];
/**
* An async version of {@link FileSystem.readFolderItemNames}.
*/
static readFolderItemNamesAsync(folderPath: string, options?: IFileSystemReadFolderOptions): Promise<string[]>;
/**
* Reads the contents of the folder, not including "." or "..", returning objects including the
* entry names and types.
* Behind the scenes it uses `fs.readdirSync()`.
* @param folderPath - The absolute or relative path to the folder which should be read.
* @param options - Optional settings that can change the behavior. Type: `IReadFolderOptions`
*/
static readFolderItems(folderPath: string, options?: IFileSystemReadFolderOptions): FolderItem[];
/**
* An async version of {@link FileSystem.readFolderItems}.
*/
static readFolderItemsAsync(folderPath: string, options?: IFileSystemReadFolderOptions): Promise<FolderItem[]>;
/**
* Deletes a folder, including all of its contents.
* Behind the scenes is uses `fs-extra.removeSync()`.
* @remarks
* Does not throw if the folderPath does not exist.
* @param folderPath - The absolute or relative path to the folder which should be deleted.
*/
static deleteFolder(folderPath: string): void;
/**
* An async version of {@link FileSystem.deleteFolder}.
*/
static deleteFolderAsync(folderPath: string): Promise<void>;
/**
* Deletes the content of a folder, but not the folder itself. Also ensures the folder exists.
* Behind the scenes it uses `fs-extra.emptyDirSync()`.
* @remarks
* This is a workaround for a common race condition, where the virus scanner holds a lock on the folder
* for a brief period after it was deleted, causing EBUSY errors for any code that tries to recreate the folder.
* @param folderPath - The absolute or relative path to the folder which should have its contents deleted.
*/
static ensureEmptyFolder(folderPath: string): void;
/**
* An async version of {@link FileSystem.ensureEmptyFolder}.
*/
static ensureEmptyFolderAsync(folderPath: string): Promise<void>;
/**
* Writes a text string to a file on disk, overwriting the file if it already exists.
* Behind the scenes it uses `fs.writeFileSync()`.
* @remarks
* Throws an error if the folder doesn't exist, unless ensureFolder=true.
* @param filePath - The absolute or relative path of the file.
* @param contents - The text that should be written to the file.
* @param options - Optional settings that can change the behavior. Type: `IWriteFileOptions`
*/
static writeFile(filePath: string, contents: string | Buffer, options?: IFileSystemWriteFileOptions): void;
/**
* Writes the contents of multiple Uint8Arrays to a file on disk, overwriting the file if it already exists.
* Behind the scenes it uses `fs.writevSync()`.
*
* This API is useful for writing large files efficiently, especially if the input is being concatenated from
* multiple sources.
*
* @remarks
* Throws an error if the folder doesn't exist, unless ensureFolder=true.
* @param filePath - The absolute or relative path of the file.
* @param contents - The content that should be written to the file.
* @param options - Optional settings that can change the behavior.
*/
static writeBuffersToFile(filePath: string, contents: ReadonlyArray<NodeJS.ArrayBufferView>, options?: IFileSystemWriteBinaryFileOptions): void;
/**
* An async version of {@link FileSystem.writeFile}.
*/
static writeFileAsync(filePath: string, contents: string | Buffer, options?: IFileSystemWriteFileOptions): Promise<void>;
/**
* An async version of {@link FileSystem.writeBuffersToFile}.
*/
static writeBuffersToFileAsync(filePath: string, contents: ReadonlyArray<NodeJS.ArrayBufferView>, options?: IFileSystemWriteBinaryFileOptions): Promise<void>;
/**
* Writes a text string to a file on disk, appending to the file if it already exists.
* Behind the scenes it uses `fs.appendFileSync()`.
* @remarks
* Throws an error if the folder doesn't exist, unless ensureFolder=true.
* @param filePath - The absolute or relative path of the file.
* @param contents - The text that should be written to the file.
* @param options - Optional settings that can change the behavior. Type: `IWriteFileOptions`
*/
static appendToFile(filePath: string, contents: string | Buffer, options?: IFileSystemWriteFileOptions): void;
/**
* An async version of {@link FileSystem.appendToFile}.
*/
static appendToFileAsync(filePath: string, contents: string | Buffer, options?: IFileSystemWriteFileOptions): Promise<void>;
/**
* Reads the contents of a file into a string.
* Behind the scenes it uses `fs.readFileSync()`.
* @param filePath - The relative or absolute path to the file whose contents should be read.
* @param options - Optional settings that can change the behavior. Type: `IReadFileOptions`
*/
static readFile(filePath: string, options?: IFileSystemReadFileOptions): string;
/**
* An async version of {@link FileSystem.readFile}.
*/
static readFileAsync(filePath: string, options?: IFileSystemReadFileOptions): Promise<string>;
/**
* Reads the contents of a file into a buffer.
* Behind the scenes is uses `fs.readFileSync()`.
* @param filePath - The relative or absolute path to the file whose contents should be read.
*/
static readFileToBuffer(filePath: string): Buffer;
/**
* An async version of {@link FileSystem.readFileToBuffer}.
*/
static readFileToBufferAsync(filePath: string): Promise<Buffer>;
/**
* Copies a single file from one location to another.
* By default, destinationPath is overwritten if it already exists.
*
* @remarks
* The `copyFile()` API cannot be used to copy folders. It copies at most one file.
* Use {@link FileSystem.copyFiles} if you need to recursively copy a tree of folders.
*
* The implementation is based on `copySync()` from the `fs-extra` package.
*/
static copyFile(options: IFileSystemCopyFileOptions): void;
/**
* An async version of {@link FileSystem.copyFile}.
*/
static copyFileAsync(options: IFileSystemCopyFileOptions): Promise<void>;
/**
* Copies a file or folder from one location to another, recursively copying any folder contents.
* By default, destinationPath is overwritten if it already exists.
*
* @remarks
* If you only intend to copy a single file, it is recommended to use {@link FileSystem.copyFile}
* instead to more clearly communicate the intended operation.
*
* The implementation is based on `copySync()` from the `fs-extra` package.
*/
static copyFiles(options: IFileSystemCopyFilesOptions): void;
/**
* An async version of {@link FileSystem.copyFiles}.
*/
static copyFilesAsync(options: IFileSystemCopyFilesAsyncOptions): Promise<void>;
/**
* Deletes a file. Can optionally throw if the file doesn't exist.
* Behind the scenes it uses `fs.unlinkSync()`.
* @param filePath - The absolute or relative path to the file that should be deleted.
* @param options - Optional settings that can change the behavior. Type: `IDeleteFileOptions`
*/
static deleteFile(filePath: string, options?: IFileSystemDeleteFileOptions): void;
/**
* An async version of {@link FileSystem.deleteFile}.
*/
static deleteFileAsync(filePath: string, options?: IFileSystemDeleteFileOptions): Promise<void>;
/**
* Gets the statistics of a filesystem object. Does NOT follow the link to its target.
* Behind the scenes it uses `fs.lstatSync()`.
* @param path - The absolute or relative path to the filesystem object.
*/
static getLinkStatistics(path: string): FileSystemStats;
/**
* An async version of {@link FileSystem.getLinkStatistics}.
*/
static getLinkStatisticsAsync(path: string): Promise<FileSystemStats>;
/**
* If `path` refers to a symbolic link, this returns the path of the link target, which may be
* an absolute or relative path.
*
* @remarks
* If `path` refers to a filesystem object that is not a symbolic link, then an `ErrnoException` is thrown
* with code 'UNKNOWN'. If `path` does not exist, then an `ErrnoException` is thrown with code `ENOENT`.
*
* @param path - The absolute or relative path to the symbolic link.
* @returns the path of the link target
*/
static readLink(path: string): string;
/**
* An async version of {@link FileSystem.readLink}.
*/
static readLinkAsync(path: string): Promise<string>;
/**
* Creates an NTFS "directory junction" on Windows operating systems; for other operating systems, it
* creates a regular symbolic link. The link target must be a folder, not a file.
* Behind the scenes it uses `fs.symlinkSync()`.
*
* @remarks
* For security reasons, Windows operating systems by default require administrator elevation to create
* symbolic links. As a result, on Windows it's generally recommended for Node.js tools to use hard links
* (for files) or NTFS directory junctions (for folders), since regular users are allowed to create them.
* Hard links and junctions are less vulnerable to symlink attacks because they cannot reference a network share,
* and their target must exist at the time of link creation. Non-Windows operating systems generally don't
* restrict symlink creation, and as such are more vulnerable to symlink attacks. Note that Windows can be
* configured to permit regular users to create symlinks, for example by enabling Windows 10 "developer mode."
*
* A directory junction requires the link source and target to both be located on local disk volumes;
* if not, use a symbolic link instead.
*/
static createSymbolicLinkJunction(options: IFileSystemCreateLinkOptions): void;
/**
* An async version of {@link FileSystem.createSymbolicLinkJunction}.
*/
static createSymbolicLinkJunctionAsync(options: IFileSystemCreateLinkOptions): Promise<void>;
/**
* Creates a symbolic link to a file. On Windows operating systems, this may require administrator elevation.
* Behind the scenes it uses `fs.symlinkSync()`.
*
* @remarks
* To avoid administrator elevation on Windows, use {@link FileSystem.createHardLink} instead.
*
* On Windows operating systems, the NTFS file system distinguishes file symlinks versus directory symlinks:
* If the target is not the correct type, the symlink will be created successfully, but will fail to resolve.
* Other operating systems do not make this distinction, in which case {@link FileSystem.createSymbolicLinkFile}
* and {@link FileSystem.createSymbolicLinkFolder} can be used interchangeably, but doing so will make your
* tool incompatible with Windows.
*/
static createSymbolicLinkFile(options: IFileSystemCreateLinkOptions): void;
/**
* An async version of {@link FileSystem.createSymbolicLinkFile}.
*/
static createSymbolicLinkFileAsync(options: IFileSystemCreateLinkOptions): Promise<void>;
/**
* Creates a symbolic link to a folder. On Windows operating systems, this may require administrator elevation.
* Behind the scenes it uses `fs.symlinkSync()`.
*
* @remarks
* To avoid administrator elevation on Windows, use {@link FileSystem.createSymbolicLinkJunction} instead.
*
* On Windows operating systems, the NTFS file system distinguishes file symlinks versus directory symlinks:
* If the target is not the correct type, the symlink will be created successfully, but will fail to resolve.
* Other operating systems do not make this distinction, in which case {@link FileSystem.createSymbolicLinkFile}
* and {@link FileSystem.createSymbolicLinkFolder} can be used interchangeably, but doing so will make your
* tool incompatible with Windows.
*/
static createSymbolicLinkFolder(options: IFileSystemCreateLinkOptions): void;
/**
* An async version of {@link FileSystem.createSymbolicLinkFolder}.
*/
static createSymbolicLinkFolderAsync(options: IFileSystemCreateLinkOptions): Promise<void>;
/**
* Creates a hard link. The link target must be a file, not a folder.
* Behind the scenes it uses `fs.linkSync()`.
*
* @remarks
* For security reasons, Windows operating systems by default require administrator elevation to create
* symbolic links. As a result, on Windows it's generally recommended for Node.js tools to use hard links
* (for files) or NTFS directory junctions (for folders), since regular users are allowed to create them.
* Hard links and junctions are less vulnerable to symlink attacks because they cannot reference a network share,
* and their target must exist at the time of link creation. Non-Windows operating systems generally don't
* restrict symlink creation, and as such are more vulnerable to symlink attacks. Note that Windows can be
* configured to permit regular users to create symlinks, for example by enabling Windows 10 "developer mode."
*
* A hard link requires the link source and target to both be located on same disk volume;
* if not, use a symbolic link instead.
*/
static createHardLink(options: IFileSystemCreateLinkOptions): void;
/**
* An async version of {@link FileSystem.createHardLink}.
*/
static createHardLinkAsync(options: IFileSystemCreateLinkOptions): Promise<void>;
/**
* Follows a link to its destination and returns the absolute path to the final target of the link.
* Behind the scenes it uses `fs.realpathSync()`.
* @param linkPath - The path to the link.
*/
static getRealPath(linkPath: string): string;
/**
* An async version of {@link FileSystem.getRealPath}.
*/
static getRealPathAsync(linkPath: string): Promise<string>;
/**
* Returns true if the error object indicates the file or folder already exists (`EEXIST`).
*/
static isExistError(error: Error): boolean;
/**
* Returns true if the error object indicates the file or folder does not exist (`ENOENT` or `ENOTDIR`)
*/
static isNotExistError(error: Error): boolean;
/**
* Returns true if the error object indicates the file does not exist (`ENOENT`).
*/
static isFileDoesNotExistError(error: Error): boolean;
/**
* Returns true if the error object indicates the folder does not exist (`ENOTDIR`).
*/
static isFolderDoesNotExistError(error: Error): boolean;
/**
* Returns true if the error object indicates the target is a directory (`EISDIR`).
*/
static isDirectoryError(error: Error): boolean;
/**
* Returns true if the error object indicates the target is not a directory (`ENOTDIR`).
*/
static isNotDirectoryError(error: Error): boolean;
/**
* Returns true if the error object indicates that the `unlink` system call failed
* due to a permissions issue (`EPERM`).
*/
static isUnlinkNotPermittedError(error: Error): boolean;
/**
* Detects if the provided error object is a `NodeJS.ErrnoException`
*/
static isErrnoException(error: Error): error is NodeJS.ErrnoException;
private static _handleLink;
private static _handleLinkAsync;
private static _wrapException;
private static _wrapExceptionAsync;
private static _updateErrorMessage;
}
//# sourceMappingURL=FileSystem.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"FileSystem.d.ts","sourceRoot":"","sources":["../src/FileSystem.ts"],"names":[],"mappings":"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 truncated
File diff suppressed because it is too large. Load diff
@@ -0,0 +1 @@
{"version":3,"file":"FileSystem.js","sourceRoot":"","sources":["../src/FileSystem.ts"],"names":[],"mappings":"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 truncated
+67
View File
@@ -0,0 +1,67 @@
import type { FileSystemStats } from './FileSystem';
/**
* Interface which represents the flags about which mode the file should be opened in.
* @public
*/
export interface IFileWriterFlags {
/**
* Open file for appending.
*/
append?: boolean;
/**
* Fails if path exists. The exclusive flag ensures that path is newly created.
*
* @remarks
* On POSIX-like operating systems, path is considered to exist even if it is a symlink to a
* non-existent file. The exclusive flag may or may not work with network file systems.
*
* POSIX is a registered trademark of the Institute of Electrical and Electronic Engineers, Inc.
*/
exclusive?: boolean;
}
/**
* API for interacting with file handles.
* @public
*/
export declare class FileWriter {
/**
* The `filePath` that was passed to {@link FileWriter.open}.
*/
readonly filePath: string;
private _fileDescriptor;
private constructor();
/**
* Opens a new file handle to the file at the specified path and given mode.
* Behind the scenes it uses `fs.openSync()`.
* The behaviour of this function is platform specific.
* See: https://nodejs.org/docs/latest-v8.x/api/fs.html#fs_fs_open_path_flags_mode_callback
* @param filePath - The absolute or relative path to the file handle that should be opened.
* @param flags - The flags for opening the handle
*/
static open(filePath: string, flags?: IFileWriterFlags): FileWriter;
/**
* Helper function to convert the file writer array to a Node.js style string (e.g. "wx" or "a").
* @param flags - The flags that should be converted.
*/
private static _convertFlagsForNode;
/**
* Writes some text to the given file handle. Throws if the file handle has been closed.
* Behind the scenes it uses `fs.writeSync()`.
* @param text - The text to write to the file.
*/
write(text: string): void;
/**
* Closes the file handle permanently. No operations can be made on this file handle after calling this.
* Behind the scenes it uses `fs.closeSync()` and releases the file descriptor to be re-used.
*
* @remarks
* The `close()` method can be called more than once; additional calls are ignored.
*/
close(): void;
/**
* Gets the statistics for the given file handle. Throws if the file handle has been closed.
* Behind the scenes it uses `fs.statSync()`.
*/
getStatistics(): FileSystemStats;
}
//# sourceMappingURL=FileWriter.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"FileWriter.d.ts","sourceRoot":"","sources":["../src/FileWriter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAWpD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,UAAU;IACrB;;OAEG;IACH,SAAgB,QAAQ,EAAE,MAAM,CAAC;IAEjC,OAAO,CAAC,eAAe,CAAqB;IAE5C,OAAO;IAKP;;;;;;;OAOG;WACW,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,gBAAgB,GAAG,UAAU;IAI1E;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,oBAAoB;IASnC;;;;OAIG;IACI,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQhC;;;;;;OAMG;IACI,KAAK,IAAI,IAAI;IAQpB;;;OAGG;IACI,aAAa,IAAI,eAAe;CAOxC"}
+73
View File
@@ -0,0 +1,73 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileWriter = void 0;
const Import_1 = require("./Import");
const fsx = Import_1.Import.lazy('fs-extra', require);
/**
* API for interacting with file handles.
* @public
*/
class FileWriter {
constructor(fileDescriptor, filePath) {
this._fileDescriptor = fileDescriptor;
this.filePath = filePath;
}
/**
* Opens a new file handle to the file at the specified path and given mode.
* Behind the scenes it uses `fs.openSync()`.
* The behaviour of this function is platform specific.
* See: https://nodejs.org/docs/latest-v8.x/api/fs.html#fs_fs_open_path_flags_mode_callback
* @param filePath - The absolute or relative path to the file handle that should be opened.
* @param flags - The flags for opening the handle
*/
static open(filePath, flags) {
return new FileWriter(fsx.openSync(filePath, FileWriter._convertFlagsForNode(flags)), filePath);
}
/**
* Helper function to convert the file writer array to a Node.js style string (e.g. "wx" or "a").
* @param flags - The flags that should be converted.
*/
static _convertFlagsForNode(flags) {
flags = Object.assign({ append: false, exclusive: false }, flags);
return [flags.append ? 'a' : 'w', flags.exclusive ? 'x' : ''].join('');
}
/**
* Writes some text to the given file handle. Throws if the file handle has been closed.
* Behind the scenes it uses `fs.writeSync()`.
* @param text - The text to write to the file.
*/
write(text) {
if (!this._fileDescriptor) {
throw new Error(`Cannot write to file, file descriptor has already been released.`);
}
fsx.writeSync(this._fileDescriptor, text);
}
/**
* Closes the file handle permanently. No operations can be made on this file handle after calling this.
* Behind the scenes it uses `fs.closeSync()` and releases the file descriptor to be re-used.
*
* @remarks
* The `close()` method can be called more than once; additional calls are ignored.
*/
close() {
const fd = this._fileDescriptor;
if (fd) {
this._fileDescriptor = undefined;
fsx.closeSync(fd);
}
}
/**
* Gets the statistics for the given file handle. Throws if the file handle has been closed.
* Behind the scenes it uses `fs.statSync()`.
*/
getStatistics() {
if (!this._fileDescriptor) {
throw new Error(`Cannot get file statistics, file descriptor has already been released.`);
}
return fsx.fstatSync(this._fileDescriptor);
}
}
exports.FileWriter = FileWriter;
//# sourceMappingURL=FileWriter.js.map
@@ -0,0 +1 @@
{"version":3,"file":"FileWriter.js","sourceRoot":"","sources":["../src/FileWriter.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAG3D,qCAAkC;AAElC,MAAM,GAAG,GAA8B,eAAM,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AA8BxE;;;GAGG;AACH,MAAa,UAAU;IAQrB,YAAoB,cAAsB,EAAE,QAAgB;QAC1D,IAAI,CAAC,eAAe,GAAG,cAAc,CAAC;QACtC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED;;;;;;;OAOG;IACI,MAAM,CAAC,IAAI,CAAC,QAAgB,EAAE,KAAwB;QAC3D,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IAClG,CAAC;IAED;;;OAGG;IACK,MAAM,CAAC,oBAAoB,CAAC,KAAmC;QACrE,KAAK,mBACH,MAAM,EAAE,KAAK,EACb,SAAS,EAAE,KAAK,IACb,KAAK,CACT,CAAC;QACF,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAkB,CAAC;IAC1F,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,IAAY;QACvB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;QACtF,CAAC;QAED,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;;OAMG;IACI,KAAK;QACV,MAAM,EAAE,GAAuB,IAAI,CAAC,eAAe,CAAC;QACpD,IAAI,EAAE,EAAE,CAAC;YACP,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;YACjC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,aAAa;QAClB,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAC;QAC5F,CAAC;QAED,OAAO,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC7C,CAAC;CACF;AA7ED,gCA6EC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport type { FileSystemStats } from './FileSystem';\nimport { Import } from './Import';\n\nconst fsx: typeof import('fs-extra') = Import.lazy('fs-extra', require);\n\n/**\n * Available file handle opening flags.\n * @public\n */\ntype NodeFileFlags = 'r' | 'r+' | 'rs+' | 'w' | 'wx' | 'w+' | 'wx+' | 'a' | 'ax' | 'a+' | 'ax+';\n\n/**\n * Interface which represents the flags about which mode the file should be opened in.\n * @public\n */\nexport interface IFileWriterFlags {\n /**\n * Open file for appending.\n */\n append?: boolean;\n\n /**\n * Fails if path exists. The exclusive flag ensures that path is newly created.\n *\n * @remarks\n * On POSIX-like operating systems, path is considered to exist even if it is a symlink to a\n * non-existent file. The exclusive flag may or may not work with network file systems.\n *\n * POSIX is a registered trademark of the Institute of Electrical and Electronic Engineers, Inc.\n */\n exclusive?: boolean;\n}\n\n/**\n * API for interacting with file handles.\n * @public\n */\nexport class FileWriter {\n /**\n * The `filePath` that was passed to {@link FileWriter.open}.\n */\n public readonly filePath: string;\n\n private _fileDescriptor: number | undefined;\n\n private constructor(fileDescriptor: number, filePath: string) {\n this._fileDescriptor = fileDescriptor;\n this.filePath = filePath;\n }\n\n /**\n * Opens a new file handle to the file at the specified path and given mode.\n * Behind the scenes it uses `fs.openSync()`.\n * The behaviour of this function is platform specific.\n * See: https://nodejs.org/docs/latest-v8.x/api/fs.html#fs_fs_open_path_flags_mode_callback\n * @param filePath - The absolute or relative path to the file handle that should be opened.\n * @param flags - The flags for opening the handle\n */\n public static open(filePath: string, flags?: IFileWriterFlags): FileWriter {\n return new FileWriter(fsx.openSync(filePath, FileWriter._convertFlagsForNode(flags)), filePath);\n }\n\n /**\n * Helper function to convert the file writer array to a Node.js style string (e.g. \"wx\" or \"a\").\n * @param flags - The flags that should be converted.\n */\n private static _convertFlagsForNode(flags: IFileWriterFlags | undefined): NodeFileFlags {\n flags = {\n append: false,\n exclusive: false,\n ...flags\n };\n return [flags.append ? 'a' : 'w', flags.exclusive ? 'x' : ''].join('') as NodeFileFlags;\n }\n\n /**\n * Writes some text to the given file handle. Throws if the file handle has been closed.\n * Behind the scenes it uses `fs.writeSync()`.\n * @param text - The text to write to the file.\n */\n public write(text: string): void {\n if (!this._fileDescriptor) {\n throw new Error(`Cannot write to file, file descriptor has already been released.`);\n }\n\n fsx.writeSync(this._fileDescriptor, text);\n }\n\n /**\n * Closes the file handle permanently. No operations can be made on this file handle after calling this.\n * Behind the scenes it uses `fs.closeSync()` and releases the file descriptor to be re-used.\n *\n * @remarks\n * The `close()` method can be called more than once; additional calls are ignored.\n */\n public close(): void {\n const fd: number | undefined = this._fileDescriptor;\n if (fd) {\n this._fileDescriptor = undefined;\n fsLine truncated
+295
View File
@@ -0,0 +1,295 @@
/**
* This interface is part of the {@link IPackageJson} file format. It is used for the
* "dependencies", "optionalDependencies", and "devDependencies" fields.
* @public
*/
export interface IPackageJsonDependencyTable {
/**
* The key is the name of a dependency. The value is a Semantic Versioning (SemVer)
* range specifier.
*/
[dependencyName: string]: string;
}
/**
* This interface is part of the {@link IPackageJson} file format. It is used for the
* "scripts" field.
* @public
*/
export interface IPackageJsonScriptTable {
/**
* The key is the name of the script hook. The value is the script body which may
* be a file path or shell script command.
*/
[scriptName: string]: string;
}
/**
* This interface is part of the {@link IPackageJson} file format. It is used for the
* "repository" field.
* @public
*/
export interface IPackageJsonRepository {
/**
* The source control type for the repository that hosts the project. This is typically "git".
*/
type: string;
/**
* The URL of the repository that hosts the project.
*/
url: string;
/**
* If the project does not exist at the root of the repository, its path is specified here.
*/
directory?: string;
}
/**
* This interface is part of the {@link IPackageJson} file format. It is used for the
* "peerDependenciesMeta" field.
* @public
*/
export interface IPeerDependenciesMetaTable {
[dependencyName: string]: {
optional?: boolean;
};
}
/**
* This interface is part of the {@link IPackageJson} file format. It is used for the
* "dependenciesMeta" field.
* @public
*/
export interface IDependenciesMetaTable {
[dependencyName: string]: {
injected?: boolean;
};
}
/**
* This interface is part of the {@link IPackageJson} file format. It is used for the values
* of the "exports" field.
*
* See {@link https://nodejs.org/api/packages.html#conditional-exports | Node.js documentation on Conditional Exports} and
* {@link https://nodejs.org/api/packages.html#community-conditions-definitions | Node.js documentation on Community Conditional Exports}.
*
* @public
*/
export interface IPackageJsonExports {
/**
* This export is like {@link IPackageJsonExports.node} in that it matches for any NodeJS environment.
* This export is specifically for native C++ addons.
*/
'node-addons'?: string | IPackageJsonExports;
/**
* This export matches for any NodeJS environment.
*/
node?: string | IPackageJsonExports;
/**
* This export matches when loaded via ESM syntax (i.e. - `import '...'` or `import('...')`).
* This is always mutually exclusive with {@link IPackageJsonExports.require}.
*/
import?: string | IPackageJsonExports;
/**
* This export matches when loaded via `require()`.
* This is always mutually exclusive with {@link IPackageJsonExports.import}.
*/
require?: string | IPackageJsonExports;
/**
* This export matches as a fallback when no other conditions match. Because exports are evaluated in
* the order that they are specified in the `package.json` file, this condition should always come last
* as no later exports will match if this one does.
*/
default?: string | IPackageJsonExports;
/**
* This export matches when loaded by the typing system (i.e. - the TypeScript compiler).
*/
types?: string | IPackageJsonExports;
/**
* Any web browser environment.
*/
browser?: string | IPackageJsonExports;
/**
* This export matches in development-only environments.
* This is always mutually exclusive with {@link IPackageJsonExports.production}.
*/
development?: string | IPackageJsonExports;
/**
* This export matches in production-only environments.
* This is always mutually exclusive with {@link IPackageJsonExports.development}.
*/
production?: string | IPackageJsonExports;
}
/**
* An interface for accessing common fields from a package.json file whose version field may be missing.
*
* @remarks
* This interface is the same as {@link IPackageJson}, except that the `version` field is optional.
* According to the {@link https://docs.npmjs.com/files/package.json | NPM documentation}
* and {@link http://wiki.commonjs.org/wiki/Packages/1.0 | CommonJS Packages specification}, the `version` field
* is normally a required field for package.json files.
*
* However, NodeJS relaxes this requirement for its `require()` API. The
* {@link https://nodejs.org/dist/latest-v10.x/docs/api/modules.html#modules_folders_as_modules
* | "Folders as Modules" section} from the NodeJS documentation gives an example of a package.json file
* that has only the `name` and `main` fields. NodeJS does not consider the `version` field during resolution,
* so it can be omitted. Some libraries do this.
*
* Use the `INodePackageJson` interface when loading such files. Use `IPackageJson` for package.json files
* that are installed from an NPM registry, or are otherwise known to have a `version` field.
*
* @public
*/
export interface INodePackageJson {
/**
* The name of the package.
*/
name: string;
/**
* A version number conforming to the Semantic Versioning (SemVer) standard.
*/
version?: string;
/**
* Indicates whether this package is allowed to be published or not.
*/
private?: boolean;
/**
* A brief description of the package.
*/
description?: string;
/**
* The URL of the project's repository.
*/
repository?: string | IPackageJsonRepository;
/**
* The URL to the project's web page.
*/
homepage?: string;
/**
* The name of the license.
*/
license?: string;
/**
* The path to the module file that will act as the main entry point.
*/
main?: string;
/**
* The path to the TypeScript *.d.ts file describing the module file
* that will act as the main entry point.
*/
types?: string;
/**
* Alias for `types`
*/
typings?: string;
/**
* The path to the TSDoc metadata file.
* This is still being standardized: https://github.com/microsoft/tsdoc/issues/7#issuecomment-442271815
* @beta
*/
tsdocMetadata?: string;
/**
* The main entry point for the package.
*/
bin?: string | Record<string, string>;
/**
* An array of dependencies that must always be installed for this package.
*/
dependencies?: IPackageJsonDependencyTable;
/**
* An array of optional dependencies that may be installed for this package.
*/
optionalDependencies?: IPackageJsonDependencyTable;
/**
* An array of dependencies that must only be installed for developers who will
* build this package.
*/
devDependencies?: IPackageJsonDependencyTable;
/**
* An array of dependencies that must be installed by a consumer of this package,
* but which will not be automatically installed by this package.
*/
peerDependencies?: IPackageJsonDependencyTable;
/**
* An array of metadata for dependencies declared inside dependencies, optionalDependencies, and devDependencies.
* https://pnpm.io/package_json#dependenciesmeta
*/
dependenciesMeta?: IDependenciesMetaTable;
/**
* An array of metadata about peer dependencies.
*/
peerDependenciesMeta?: IPeerDependenciesMetaTable;
/**
* A table of script hooks that a package manager or build tool may invoke.
*/
scripts?: IPackageJsonScriptTable;
/**
* A table of package version resolutions. This feature is only implemented by the Yarn package manager.
*
* @remarks
* See the {@link https://github.com/yarnpkg/rfcs/blob/master/implemented/0000-selective-versions-resolutions.md
* | 0000-selective-versions-resolutions.md RFC} for details.
*/
resolutions?: Record<string, string>;
/**
* A table of TypeScript *.d.ts file paths that are compatible with specific TypeScript version
* selectors. This data take a form similar to that of the {@link INodePackageJson.exports} field,
* with fallbacks listed in order in the value array for example:
*
* ```JSON
* "typesVersions": {
* ">=3.1": {
* "*": ["./types-3.1/*", "./types-3.1-fallback/*"]
* },
* ">=3.0": {
* "*": ["./types-legacy/*"]
* }
* }
* ```
*
* or
*
* ```JSON
* "typesVersions": {
* ">=3.1": {
* "app/*": ["./app/types-3.1/*"],
* "lib/*": ["./lib/types-3.1/*"]
* },
* ">=3.0": {
* "app/*": ["./app/types-legacy/*"],
* "lib/*": ["./lib/types-legacy/*"]
* }
* }
* ```
*
* See the
* {@link https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#version-selection-with-typesversions
* | TypeScript documentation} for details.
*/
typesVersions?: Record<string, Record<string, [string, ...string[]]>>;
/**
* The "exports" field is used to specify the entry points for a package.
* See {@link https://nodejs.org/api/packages.html#exports | Node.js documentation}
*/
exports?: string | string[] | Record<string, null | string | IPackageJsonExports>;
/**
* The "files" field is an array of file globs that should be included in the package during publishing.
*
* See the {@link https://docs.npmjs.com/cli/v6/configuring-npm/package-json#files | NPM documentation}.
*/
files?: string[];
}
/**
* An interface for accessing common fields from a package.json file.
*
* @remarks
* This interface describes a package.json file format whose `name` and `version` field are required.
* In some situations, the `version` field is optional; in that case, use the {@link INodePackageJson}
* interface instead.
*
* More fields may be added to this interface in the future. For documentation about the package.json file format,
* see the {@link http://wiki.commonjs.org/wiki/Packages/1.0 | CommonJS Packages specification}
* and the {@link https://docs.npmjs.com/files/package.json | NPM manual page}.
*
* @public
*/
export interface IPackageJson extends INodePackageJson {
/** {@inheritDoc INodePackageJson.version} */
version: string;
}
//# sourceMappingURL=IPackageJson.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"IPackageJson.d.ts","sourceRoot":"","sources":["../src/IPackageJson.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;OAGG;IACH,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC;CAClC;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,MAAM,WAAW,0BAA0B;IACzC,CAAC,cAAc,EAAE,MAAM,GAAG;QACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;CACH;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,CAAC,cAAc,EAAE,MAAM,GAAG;QACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IAE7C;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IAEpC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IAEtC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IAEvC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IAEvC;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IAErC;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IAEvC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;IAE3C;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,mBAAmB,CAAC;CAC3C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,sBAAsB,CAAC;IAE7C;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEtC;;OAEG;IACH,YAAY,CAAC,EAAE,2BAA2B,CAAC;IAE3C;;OAEG;IACH,oBAAoB,CAAC,EAAE,2BAA2B,CAAC;IAEnD;;;OAGG;IACH,eAAe,CAAC,EAAE,2BAA2B,CAAC;IAE9C;;;OAGG;IACH,gBAAgB,CAAC,EAAE,2BAA2B,CAAC;IAE/C;;;OAGG;IACH,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;IAE1C;;OAEG;IACH,oBAAoB,CAAC,EAAE,0BAA0B,CAAC;IAElD;;OAEG;IACH,OAAO,CAAC,EAAE,uBAAuB,CAAC;IAElC;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IAEtE;;;OAGG;IAEH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,mBAAmB,CAAC,CAAC;IAElF;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAa,SAAQ,gBAAgB;IAEpD,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;CACjB"}
+5
View File
@@ -0,0 +1,5 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=IPackageJson.js.map
@@ -0,0 +1 @@
{"version":3,"file":"IPackageJson.js","sourceRoot":"","sources":["../src/IPackageJson.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n/**\n * This interface is part of the {@link IPackageJson} file format. It is used for the\n * \"dependencies\", \"optionalDependencies\", and \"devDependencies\" fields.\n * @public\n */\nexport interface IPackageJsonDependencyTable {\n /**\n * The key is the name of a dependency. The value is a Semantic Versioning (SemVer)\n * range specifier.\n */\n [dependencyName: string]: string;\n}\n\n/**\n * This interface is part of the {@link IPackageJson} file format. It is used for the\n * \"scripts\" field.\n * @public\n */\nexport interface IPackageJsonScriptTable {\n /**\n * The key is the name of the script hook. The value is the script body which may\n * be a file path or shell script command.\n */\n [scriptName: string]: string;\n}\n\n/**\n * This interface is part of the {@link IPackageJson} file format. It is used for the\n * \"repository\" field.\n * @public\n */\nexport interface IPackageJsonRepository {\n /**\n * The source control type for the repository that hosts the project. This is typically \"git\".\n */\n type: string;\n\n /**\n * The URL of the repository that hosts the project.\n */\n url: string;\n\n /**\n * If the project does not exist at the root of the repository, its path is specified here.\n */\n directory?: string;\n}\n\n/**\n * This interface is part of the {@link IPackageJson} file format. It is used for the\n * \"peerDependenciesMeta\" field.\n * @public\n */\nexport interface IPeerDependenciesMetaTable {\n [dependencyName: string]: {\n optional?: boolean;\n };\n}\n\n/**\n * This interface is part of the {@link IPackageJson} file format. It is used for the\n * \"dependenciesMeta\" field.\n * @public\n */\nexport interface IDependenciesMetaTable {\n [dependencyName: string]: {\n injected?: boolean;\n };\n}\n\n/**\n * This interface is part of the {@link IPackageJson} file format. It is used for the values\n * of the \"exports\" field.\n *\n * See {@link https://nodejs.org/api/packages.html#conditional-exports | Node.js documentation on Conditional Exports} and\n * {@link https://nodejs.org/api/packages.html#community-conditions-definitions | Node.js documentation on Community Conditional Exports}.\n *\n * @public\n */\nexport interface IPackageJsonExports {\n /**\n * This export is like {@link IPackageJsonExports.node} in that it matches for any NodeJS environment.\n * This export is specifically for native C++ addons.\n */\n 'node-addons'?: string | IPackageJsonExports;\n\n /**\n * This export matches for any NodeJS environment.\n */\n node?: string | IPackageJsonExports;\n\n /**\n * This export matches when loaded via ESM syntax (i.e. - `import '...'` or `import('...')`).\n * This is always mutually exclusive with {@link IPackageJsonExports.require}.\n */\n import?: string | IPackageJsonExports;\n\n /**\n * This export matches when loaded via `require()`.\n * This is always mutually exclusive with {@link IPackageJsonExports.import}.\n */\n require?: string | IPackageJsonExports;\n\n /**\n * This export matches as a fallback when no other conditions match. Because exports are evaluated in\n * the order that they are specified in the `package.json` file, this condition should always come last\n * as no later exports will match if this one does.\n */\n default?: string | IPackageJsonExports;\n\n /**\n * This export matches when loaded by the typing system (i.e. - the TypeScript compiler).\n */\n types?: string | IPackageJsonExports;\n\n /**\n * Any web browser environment.\n */\n browser?: string | IPackageJsonExports;\n\n /**\n * This export matches in development-only environments.\n * This is always mutually exclusive with {@link IPackageJsonExports.production}.\n */\n development?: string | IPackageJsonExports;\n\n /**\n * This export matches in production-only environments.\n * This is always mutually exclusive with {@link IPackageJsonExports.development}.\n */\n production?: string | IPackageJsonExports;\n}\n\n/**\n * An interface for accessing common fields from a package.json file whose version field may be missing.\n *\n * @remarks\n * This interface is the same as {@link IPackageJson}, except that the `version` field is optional.\n * According to the {@link https://docs.npmjs.com/files/package.json | NPM documentation}\n * and {@link http://wiki.commonjs.org/wiki/Packages/1.0 | CommonJS Packages specification}, the `version` field\n * is normally a required field for package.json files.\n *\n * However, NodeJS relaxes this requirement for its `require()` API. The\n * {@link https://nodejs.org/dist/latest-v10.x/docs/api/modules.html#modules_folderLine truncated
+255
View File
@@ -0,0 +1,255 @@
/**
* Common options shared by {@link IImportResolveModuleOptions} and {@link IImportResolvePackageOptions}
* @public
*/
export interface IImportResolveOptions {
/**
* The path from which {@link IImportResolveModuleOptions.modulePath} or
* {@link IImportResolvePackageOptions.packageName} should be resolved.
*/
baseFolderPath: string;
/**
* If true, if the package name matches a Node.js system module, then the return
* value will be the package name without any path.
*
* @remarks
* This will take precedence over an installed NPM package of the same name.
*
* Example:
* ```ts
* // Returns the string "fs" indicating the Node.js system module
* Import.resolveModulePath({
* resolvePath: "fs",
* basePath: process.cwd()
* })
* ```
*/
includeSystemModules?: boolean;
/**
* If true, then resolvePath is allowed to refer to the package.json of the active project.
*
* @remarks
* This will take precedence over any installed dependency with the same name.
* Note that this requires an additional PackageJsonLookup calculation.
*
* Example:
* ```ts
* // Returns an absolute path to the current package
* Import.resolveModulePath({
* resolvePath: "current-project",
* basePath: process.cwd(),
* allowSelfReference: true
* })
* ```
*/
allowSelfReference?: boolean;
/**
* A function used to resolve the realpath of a provided file path.
*
* @remarks
* This is used to resolve symlinks and other non-standard file paths. By default, this uses the
* {@link FileSystem.getRealPath} function. However, it can be overridden to use a custom implementation
* which may be faster, more accurate, or provide support for additional non-standard file paths.
*/
getRealPath?: (filePath: string) => string;
}
/**
* Common options shared by {@link IImportResolveModuleAsyncOptions} and {@link IImportResolvePackageAsyncOptions}
* @public
*/
export interface IImportResolveAsyncOptions extends IImportResolveOptions {
/**
* A function used to resolve the realpath of a provided file path.
*
* @remarks
* This is used to resolve symlinks and other non-standard file paths. By default, this uses the
* {@link FileSystem.getRealPath} function. However, it can be overridden to use a custom implementation
* which may be faster, more accurate, or provide support for additional non-standard file paths.
*/
getRealPathAsync?: (filePath: string) => Promise<string>;
}
/**
* Options for {@link 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 {@link Import.resolveModuleAsync}
* @public
*/
export interface IImportResolveModuleAsyncOptions extends IImportResolveAsyncOptions {
/**
* The module identifier to resolve. For example "\@rushstack/node-core-library" or
* "\@rushstack/node-core-library/lib/index.js"
*/
modulePath: string;
}
/**
* Options for {@link Import.resolvePackage}
* @public
*/
export interface IImportResolvePackageOptions extends IImportResolveOptions {
/**
* The package name to resolve. For example "\@rushstack/node-core-library"
*/
packageName: string;
/**
* If true, then the module path will be resolved using Node.js's built-in resolution algorithm.
*
* @remarks
* This allows reusing Node's built-in resolver cache.
* This implies `allowSelfReference: true`. The passed `getRealPath` will only be used on `baseFolderPath`.
*/
useNodeJSResolver?: boolean;
}
/**
* Options for {@link Import.resolvePackageAsync}
* @public
*/
export interface IImportResolvePackageAsyncOptions extends IImportResolveAsyncOptions {
/**
* The package name to resolve. For example "\@rushstack/node-core-library"
*/
packageName: string;
}
/**
* Helpers for resolving and importing Node.js modules.
* @public
*/
export declare class Import {
private static __builtInModules;
private static get _builtInModules();
/**
* Provides a way to improve process startup times by lazy-loading imported modules.
*
* @remarks
* This is a more structured wrapper for the {@link https://www.npmjs.com/package/import-lazy|import-lazy}
* 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);
* ```
*/
static lazy(moduleName: string, require: (id: string) => unknown): any;
/**
* This resolves a module path using similar logic as the Node.js `require.resolve()` API,
* but supporting extra features such as specifying the base folder.
*
* @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 {@link Import.resolvePackage} instead.
*
* @returns the absolute path of the resolved module.
* If {@link IImportResolveOptions.includeSystemModules} is specified
* and a system module is found, then its name is returned without any file path.
*/
static resolveModule(options: IImportResolveModuleOptions): string;
/**
* Async version of {@link Import.resolveModule}.
*/
static resolveModuleAsync(options: IImportResolveModuleAsyncOptions): Promise<string>;
/**
* Performs module resolution to determine the folder where a package is installed.
*
* @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 {@link 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 {@link IImportResolveOptions.includeSystemModules} is specified
* and a system module is found, then its name is returned without any file path.
*/
static resolvePackage(options: IImportResolvePackageOptions): string;
/**
* Async version of {@link Import.resolvePackage}.
*/
static resolvePackageAsync(options: IImportResolvePackageAsyncOptions): Promise<string>;
private static _getPackageName;
}
//# sourceMappingURL=Import.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Import.d.ts","sourceRoot":"","sources":["../src/Import.ts"],"names":[],"mappings":"AAeA;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IAEvB;;;;;;;;;;;;;;;OAeG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAE/B;;;;;;;;;;;;;;;;OAgBG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;CAC5C;AAED;;;GAGG;AACH,MAAM,WAAW,0BAA2B,SAAQ,qBAAqB;IACvE;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1D;AAED;;;GAGG;AACH,MAAM,WAAW,2BAA4B,SAAQ,qBAAqB;IACxE;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,gCAAiC,SAAQ,0BAA0B;IAClF;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,4BAA6B,SAAQ,qBAAqB;IACzE;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,iCAAkC,SAAQ,0BAA0B;IACnF;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAOD;;;GAGG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAA0B;IACzD,OAAO,CAAC,MAAM,KAAK,eAAe,GAMjC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqEG;WAEW,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,GAAG;IAK7E;;;;;;;;;;;;;;;;;;;;;;;OAuBG;WACW,aAAa,CAAC,OAAO,EAAE,2BAA2B,GAAG,MAAM;IA4CzE;;OAEG;WACiB,kBAAkB,CAAC,OAAO,EAAE,gCAAgC,GAAG,OAAO,CAAC,MAAM,CAAC;IAwFlG;;;;;;;;;;;;;;;;;;;OAmBG;WACW,cAAc,CAAC,OAAO,EAAE,4BAA4B,GAAG,MAAM;IA6C3E;;OAEG;WACiB,mBAAmB,CAAC,OAAO,EAAE,iCAAiC,GAAG,OAAO,CAAC,MAAM,CAAC;IA8EpG,OAAO,CAAC,MAAM,CAAC,eAAe;CAa/B"}
+390
View File
@@ -0,0 +1,390 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Import = void 0;
const path = __importStar(require("path"));
const importLazy = require("import-lazy");
const Resolve = __importStar(require("resolve"));
const nodeModule = require("module");
const PackageJsonLookup_1 = require("./PackageJsonLookup");
const FileSystem_1 = require("./FileSystem");
const PackageName_1 = require("./PackageName");
/**
* Helpers for resolving and importing Node.js modules.
* @public
*/
class Import {
static get _builtInModules() {
if (!Import.__builtInModules) {
Import.__builtInModules = new Set(nodeModule.builtinModules);
}
return Import.__builtInModules;
}
/**
* Provides a way to improve process startup times by lazy-loading imported modules.
*
* @remarks
* This is a more structured wrapper for the {@link https://www.npmjs.com/package/import-lazy|import-lazy}
* 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);
* ```
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static lazy(moduleName, require) {
const importLazyLocal = importLazy(require);
return importLazyLocal(moduleName);
}
/**
* This resolves a module path using similar logic as the Node.js `require.resolve()` API,
* but supporting extra features such as specifying the base folder.
*
* @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 {@link Import.resolvePackage} instead.
*
* @returns the absolute path of the resolved module.
* If {@link IImportResolveOptions.includeSystemModules} is specified
* and a system module is found, then its name is returned without any file path.
*/
static resolveModule(options) {
const { modulePath, baseFolderPath, includeSystemModules, allowSelfReference, getRealPath } = options;
if (path.isAbsolute(modulePath)) {
return modulePath;
}
const normalizedRootPath = (getRealPath || FileSystem_1.FileSystem.getRealPath)(baseFolderPath);
if (modulePath.startsWith('.')) {
// This looks like a conventional relative path
return path.resolve(normalizedRootPath, modulePath);
}
// Built-in modules do not have a scope, so if there is a slash, then we need to check
// against the first path segment
const slashIndex = modulePath.indexOf('/');
const moduleName = slashIndex === -1 ? modulePath : modulePath.slice(0, slashIndex);
if (!includeSystemModules && Import._builtInModules.has(moduleName)) {
throw new Error(`Cannot find module "${modulePath}" from "${options.baseFolderPath}".`);
}
if (allowSelfReference === true) {
const ownPackage = Import._getPackageName(normalizedRootPath);
if (ownPackage &&
(modulePath === ownPackage.packageName || modulePath.startsWith(`${ownPackage.packageName}/`))) {
const packagePath = modulePath.slice(ownPackage.packageName.length + 1);
return path.resolve(ownPackage.packageRootPath, packagePath);
}
}
try {
return Resolve.sync(modulePath, {
basedir: normalizedRootPath,
preserveSymlinks: false,
realpathSync: getRealPath
});
}
catch (e) {
throw new Error(`Cannot find module "${modulePath}" from "${options.baseFolderPath}": ${e}`);
}
}
/**
* Async version of {@link Import.resolveModule}.
*/
static async resolveModuleAsync(options) {
const { modulePath, baseFolderPath, includeSystemModules, allowSelfReference, getRealPath, getRealPathAsync } = options;
if (path.isAbsolute(modulePath)) {
return modulePath;
}
const normalizedRootPath = await (getRealPathAsync || getRealPath || FileSystem_1.FileSystem.getRealPathAsync)(baseFolderPath);
if (modulePath.startsWith('.')) {
// This looks like a conventional relative path
return path.resolve(normalizedRootPath, modulePath);
}
// Built-in modules do not have a scope, so if there is a slash, then we need to check
// against the first path segment
const slashIndex = modulePath.indexOf('/');
const moduleName = slashIndex === -1 ? modulePath : modulePath.slice(0, slashIndex);
if (!includeSystemModules && Import._builtInModules.has(moduleName)) {
throw new Error(`Cannot find module "${modulePath}" from "${options.baseFolderPath}".`);
}
if (allowSelfReference === true) {
const ownPackage = Import._getPackageName(normalizedRootPath);
if (ownPackage &&
(modulePath === ownPackage.packageName || modulePath.startsWith(`${ownPackage.packageName}/`))) {
const packagePath = modulePath.slice(ownPackage.packageName.length + 1);
return path.resolve(ownPackage.packageRootPath, packagePath);
}
}
try {
const resolvePromise = new Promise((resolve, reject) => {
const realPathFn = getRealPathAsync || getRealPath
? (filePath, callback) => {
if (getRealPathAsync) {
getRealPathAsync(filePath)
.then((resolvedPath) => callback(null, resolvedPath))
.catch((error) => callback(error));
}
else {
try {
const resolvedPath = getRealPath(filePath);
callback(null, resolvedPath);
}
catch (error) {
callback(error);
}
}
}
: undefined;
Resolve.default(modulePath, {
basedir: normalizedRootPath,
preserveSymlinks: false,
realpath: realPathFn
}, (error, resolvedPath) => {
if (error) {
reject(error);
}
else {
// Resolve docs state that either an error will be returned, or the resolved path.
// In this case, the resolved path should always be populated.
resolve(resolvedPath);
}
});
});
return await resolvePromise;
}
catch (e) {
throw new Error(`Cannot find module "${modulePath}" from "${options.baseFolderPath}": ${e}`);
}
}
/**
* Performs module resolution to determine the folder where a package is installed.
*
* @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 {@link 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 {@link IImportResolveOptions.includeSystemModules} is specified
* and a system module is found, then its name is returned without any file path.
*/
static resolvePackage(options) {
const { packageName, includeSystemModules, baseFolderPath, allowSelfReference, getRealPath, useNodeJSResolver } = options;
if (includeSystemModules && Import._builtInModules.has(packageName)) {
return packageName;
}
const normalizedRootPath = (getRealPath || FileSystem_1.FileSystem.getRealPath)(baseFolderPath);
if (allowSelfReference) {
const ownPackage = Import._getPackageName(normalizedRootPath);
if (ownPackage && ownPackage.packageName === packageName) {
return ownPackage.packageRootPath;
}
}
PackageName_1.PackageName.parse(packageName); // Ensure the package name is valid and doesn't contain a path
try {
const resolvedPath = useNodeJSResolver
? require.resolve(`${packageName}/package.json`, {
paths: [normalizedRootPath]
})
: // Append `/package.json` to ensure `resolve.sync` doesn't attempt to return a system package, and to avoid
// having to mess with the `packageFilter` option.
Resolve.sync(`${packageName}/package.json`, {
basedir: normalizedRootPath,
preserveSymlinks: false,
realpathSync: getRealPath
});
const packagePath = path.dirname(resolvedPath);
return packagePath;
}
catch (e) {
throw new Error(`Cannot find package "${packageName}" from "${baseFolderPath}": ${e}.`);
}
}
/**
* Async version of {@link Import.resolvePackage}.
*/
static async resolvePackageAsync(options) {
const { packageName, includeSystemModules, baseFolderPath, allowSelfReference, getRealPath, getRealPathAsync } = options;
if (includeSystemModules && Import._builtInModules.has(packageName)) {
return packageName;
}
const normalizedRootPath = await (getRealPathAsync || getRealPath || FileSystem_1.FileSystem.getRealPathAsync)(baseFolderPath);
if (allowSelfReference) {
const ownPackage = Import._getPackageName(normalizedRootPath);
if (ownPackage && ownPackage.packageName === packageName) {
return ownPackage.packageRootPath;
}
}
PackageName_1.PackageName.parse(packageName); // Ensure the package name is valid and doesn't contain a path
try {
const resolvePromise = new Promise((resolve, reject) => {
const realPathFn = getRealPathAsync || getRealPath
? (filePath, callback) => {
if (getRealPathAsync) {
getRealPathAsync(filePath)
.then((resolvedPath) => callback(null, resolvedPath))
.catch((error) => callback(error));
}
else {
try {
const resolvedPath = getRealPath(filePath);
callback(null, resolvedPath);
}
catch (error) {
callback(error);
}
}
}
: undefined;
Resolve.default(
// Append `/package.json` to ensure `resolve` doesn't attempt to return a system package, and to avoid
// having to mess with the `packageFilter` option.
`${packageName}/package.json`, {
basedir: normalizedRootPath,
preserveSymlinks: false,
realpath: realPathFn
}, (error, resolvedPath) => {
if (error) {
reject(error);
}
else {
// Resolve docs state that either an error will be returned, or the resolved path.
// In this case, the resolved path should always be populated.
resolve(resolvedPath);
}
});
});
const resolvedPath = await resolvePromise;
const packagePath = path.dirname(resolvedPath);
return packagePath;
}
catch (e) {
throw new Error(`Cannot find package "${packageName}" from "${baseFolderPath}": ${e}`);
}
}
static _getPackageName(rootPath) {
const packageJsonPath = PackageJsonLookup_1.PackageJsonLookup.instance.tryGetPackageJsonFilePathFor(rootPath);
if (packageJsonPath) {
const packageJson = PackageJsonLookup_1.PackageJsonLookup.instance.loadPackageJson(packageJsonPath);
return {
packageRootPath: path.dirname(packageJsonPath),
packageName: packageJson.name
};
}
else {
return undefined;
}
}
}
exports.Import = Import;
//# sourceMappingURL=Import.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Import.js","sourceRoot":"","sources":["../src/Import.ts"],"names":[],"mappings":"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 truncated
@@ -0,0 +1,36 @@
/**
* An `Error` subclass that should be thrown to report an unexpected state that may indicate a software defect.
* An application may handle this error by instructing the end user to report an issue to the application maintainers.
*
* @remarks
* Do not use this class unless you intend to solicit bug reports from end users.
*
* @public
*/
export declare class InternalError extends Error {
/**
* If true, a JavScript `debugger;` statement will be invoked whenever the `InternalError` constructor is called.
*
* @remarks
* Generally applications should not be catching and ignoring an `InternalError`. Instead, the error should
* be reported and typically the application will terminate. Thus, if `InternalError` is constructed, it's
* almost always something we want to examine in a debugger.
*/
static breakInDebugger: boolean;
/**
* The underlying error message, without the additional boilerplate for an `InternalError`.
*/
readonly unformattedMessage: string;
/**
* Constructs a new instance of the {@link InternalError} class.
*
* @param message - A message describing the error. This will be assigned to
* {@link InternalError.unformattedMessage}. The `Error.message` field will have additional boilerplate
* explaining that the user has encountered a software defect.
*/
constructor(message: string);
private static _formatMessage;
/** @override */
toString(): string;
}
//# sourceMappingURL=InternalError.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"InternalError.d.ts","sourceRoot":"","sources":["../src/InternalError.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC;;;;;;;OAOG;IACH,OAAc,eAAe,EAAE,OAAO,CAAQ;IAE9C;;OAEG;IACH,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAE3C;;;;;;OAMG;gBACgB,OAAO,EAAE,MAAM;IAiBlC,OAAO,CAAC,MAAM,CAAC,cAAc;IAO7B,gBAAgB;IACT,QAAQ,IAAI,MAAM;CAG1B"}
+55
View File
@@ -0,0 +1,55 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.InternalError = void 0;
/**
* An `Error` subclass that should be thrown to report an unexpected state that may indicate a software defect.
* An application may handle this error by instructing the end user to report an issue to the application maintainers.
*
* @remarks
* Do not use this class unless you intend to solicit bug reports from end users.
*
* @public
*/
class InternalError extends Error {
/**
* Constructs a new instance of the {@link InternalError} class.
*
* @param message - A message describing the error. This will be assigned to
* {@link InternalError.unformattedMessage}. The `Error.message` field will have additional boilerplate
* explaining that the user has encountered a software defect.
*/
constructor(message) {
super(InternalError._formatMessage(message));
// Manually set the prototype, as we can no longer extend built-in classes like Error, Array, Map, etc.
// https://github.com/microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
//
// Note: the prototype must also be set on any classes which extend this one
this.__proto__ = InternalError.prototype; // eslint-disable-line @typescript-eslint/no-explicit-any
this.unformattedMessage = message;
if (InternalError.breakInDebugger) {
// eslint-disable-next-line no-debugger
debugger;
}
}
static _formatMessage(unformattedMessage) {
return (`Internal Error: ${unformattedMessage}\n\nYou have encountered a software defect. Please consider` +
` reporting the issue to the maintainers of this application.`);
}
/** @override */
toString() {
return this.message; // Avoid adding the "Error:" prefix
}
}
exports.InternalError = InternalError;
/**
* If true, a JavScript `debugger;` statement will be invoked whenever the `InternalError` constructor is called.
*
* @remarks
* Generally applications should not be catching and ignoring an `InternalError`. Instead, the error should
* be reported and typically the application will terminate. Thus, if `InternalError` is constructed, it's
* almost always something we want to examine in a debugger.
*/
InternalError.breakInDebugger = true;
//# sourceMappingURL=InternalError.js.map
@@ -0,0 +1 @@
{"version":3,"file":"InternalError.js","sourceRoot":"","sources":["../src/InternalError.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D;;;;;;;;GAQG;AACH,MAAa,aAAc,SAAQ,KAAK;IAgBtC;;;;;;OAMG;IACH,YAAmB,OAAe;QAChC,KAAK,CAAC,aAAa,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;QAE7C,uGAAuG;QACvG,6IAA6I;QAC7I,EAAE;QACF,4EAA4E;QAC3E,IAAY,CAAC,SAAS,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC,yDAAyD;QAE5G,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC;QAElC,IAAI,aAAa,CAAC,eAAe,EAAE,CAAC;YAClC,uCAAuC;YACvC,QAAQ,CAAC;QACX,CAAC;IACH,CAAC;IAEO,MAAM,CAAC,cAAc,CAAC,kBAA0B;QACtD,OAAO,CACL,mBAAmB,kBAAkB,6DAA6D;YAClG,8DAA8D,CAC/D,CAAC;IACJ,CAAC;IAED,gBAAgB;IACT,QAAQ;QACb,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,mCAAmC;IAC1D,CAAC;;AAlDH,sCAmDC;AAlDC;;;;;;;GAOG;AACW,6BAAe,GAAY,IAAI,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n/**\n * An `Error` subclass that should be thrown to report an unexpected state that may indicate a software defect.\n * An application may handle this error by instructing the end user to report an issue to the application maintainers.\n *\n * @remarks\n * Do not use this class unless you intend to solicit bug reports from end users.\n *\n * @public\n */\nexport class InternalError extends Error {\n /**\n * If true, a JavScript `debugger;` statement will be invoked whenever the `InternalError` constructor is called.\n *\n * @remarks\n * Generally applications should not be catching and ignoring an `InternalError`. Instead, the error should\n * be reported and typically the application will terminate. Thus, if `InternalError` is constructed, it's\n * almost always something we want to examine in a debugger.\n */\n public static breakInDebugger: boolean = true;\n\n /**\n * The underlying error message, without the additional boilerplate for an `InternalError`.\n */\n public readonly unformattedMessage: string;\n\n /**\n * Constructs a new instance of the {@link InternalError} class.\n *\n * @param message - A message describing the error. This will be assigned to\n * {@link InternalError.unformattedMessage}. The `Error.message` field will have additional boilerplate\n * explaining that the user has encountered a software defect.\n */\n public constructor(message: string) {\n super(InternalError._formatMessage(message));\n\n // Manually set the prototype, as we can no longer extend built-in classes like Error, Array, Map, etc.\n // https://github.com/microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work\n //\n // Note: the prototype must also be set on any classes which extend this one\n (this as any).__proto__ = InternalError.prototype; // eslint-disable-line @typescript-eslint/no-explicit-any\n\n this.unformattedMessage = message;\n\n if (InternalError.breakInDebugger) {\n // eslint-disable-next-line no-debugger\n debugger;\n }\n }\n\n private static _formatMessage(unformattedMessage: string): string {\n return (\n `Internal Error: ${unformattedMessage}\\n\\nYou have encountered a software defect. Please consider` +\n ` reporting the issue to the maintainers of this application.`\n );\n }\n\n /** @override */\n public toString(): string {\n return this.message; // Avoid adding the \"Error:\" prefix\n }\n}\n"]}
+252
View File
@@ -0,0 +1,252 @@
import type { JsonSchema, IJsonSchemaErrorInfo, IJsonSchemaValidateOptions } from './JsonSchema';
import { type NewlineKind } from './Text';
/**
* Represents a JSON-serializable object whose type has not been determined yet.
*
* @remarks
*
* This type is similar to `any`, except that it communicates that the object is serializable JSON.
*
* @public
*/
export type JsonObject = any;
/**
* The Rush Stack lint rules discourage usage of `null`. However, JSON parsers always return JavaScript's
* `null` to keep the two syntaxes consistent. When creating interfaces that describe JSON structures,
* use `JsonNull` to avoid triggering the lint rule. Do not use `JsonNull` for any other purpose.
*
* @remarks
* If you are designing a new JSON file format, it's a good idea to avoid `null` entirely. In most cases
* there are better representations that convey more information about an item that is unknown, omitted, or disabled.
*
* To understand why `null` is deprecated, please see the `@rushstack/eslint-plugin` documentation here:
*
* {@link https://www.npmjs.com/package/@rushstack/eslint-plugin#rushstackno-null}
*
* @public
*/
export type JsonNull = null;
/**
* Specifies the variant of JSON syntax to be used.
*
* @public
*/
export declare enum JsonSyntax {
/**
* Specifies the exact RFC 8259 format as implemented by the `JSON.parse()` system API.
* This format was designed for machine generated inputs such as an HTTP payload.
* It is not a recommend choice for human-authored files, because it does not support
* code comments.
*
* @remarks
*
* A well-known quote from Douglas Crockford, the inventor of JSON:
*
* "I removed comments from JSON because I saw people were using them to hold parsing directives,
* a practice which would have destroyed interoperability. I know that the lack of comments makes
* some people sad, but it shouldn't. Suppose you are using JSON to keep configuration files,
* which you would like to annotate. Go ahead and insert all the comments you like.
* Then pipe it through JSMin before handing it to your JSON parser."
*
* @see {@link https://datatracker.ietf.org/doc/html/rfc8259 | RFC 8259}
*/
Strict = "strict",
/**
* `JsonSyntax.JsonWithComments` is the recommended format for human-authored config files.
* It is a minimal extension to `JsonSyntax.Strict` adding support for code comments
* using `//` and `/*`.
*
* @remarks
*
* VS Code calls this format `jsonc`, but it should not be confused with unrelated file formats
* and libraries that also use the name "JSONC".
*
* To fix VS Code syntax highlighting, add this setting:
* `"files.associations": { "*.json": "jsonc" }`
*
* To fix GitHub syntax highlighting, add this to your `.gitattributes`:
* `*.json linguist-language=JSON-with-Comments`
*/
JsonWithComments = "jsonWithComments",
/**
* JSON5 is a project that proposes a JSON-like format supplemented with ECMAScript 5.1
* notations for objects, numbers, comments, and more.
*
* @remarks
* Files using this format should use the `.json5` file extension instead of `.json`.
*
* JSON5 has substantial differences from JSON: object keys may be unquoted, trailing commas
* are allowed, and strings may span multiple lines. Whereas {@link JsonSyntax.JsonWithComments} can
* be cheaply converted to standard JSON by stripping comments, parsing JSON5 requires a
* nontrivial algorithm that may not be easily available in some contexts or programming languages.
*
* @see {@link https://json5.org/ | JSON5 project website}
*/
Json5 = "json5"
}
/**
* Options for {@link JsonFile.parseString}, {@link JsonFile.load}, and {@link JsonFile.loadAsync}.
*
* @public
*/
export interface IJsonFileParseOptions {
/**
* Specifies the variant of JSON syntax to be used.
*
* @defaultValue
* {@link JsonSyntax.Json5}
*
* NOTE: This default will be changed to `JsonSyntax.JsonWithComments` in a future release.
*/
jsonSyntax?: JsonSyntax;
}
/**
* Options for {@link JsonFile.loadAndValidate} and {@link JsonFile.loadAndValidateAsync}
*
* @public
*/
export interface IJsonFileLoadAndValidateOptions extends IJsonFileParseOptions, IJsonSchemaValidateOptions {
}
/**
* Options for {@link JsonFile.stringify}
*
* @public
*/
export interface IJsonFileStringifyOptions extends IJsonFileParseOptions {
/**
* If provided, the specified newline type will be used instead of the default `\r\n`.
*/
newlineConversion?: NewlineKind;
/**
* By default, {@link JsonFile.stringify} validates that the object does not contain any
* keys whose value is `undefined`. To disable this validation, set
* {@link IJsonFileStringifyOptions.ignoreUndefinedValues} to `true`
* which causes such keys to be silently discarded, consistent with the system `JSON.stringify()`.
*
* @remarks
*
* The JSON file format can represent `null` values ({@link JsonNull}) but not `undefined` values.
* In ECMAScript code however, we generally avoid `null` and always represent empty states
* as `undefined`, because it is the default value of missing/uninitialized variables.
* (In practice, distinguishing "null" versus "uninitialized" has more drawbacks than benefits.)
* This poses a problem when serializing ECMAScript objects that contain `undefined` members.
* As a safeguard, {@link JsonFile} will report an error if any `undefined` values are encountered
* during serialization. Set {@link IJsonFileStringifyOptions.ignoreUndefinedValues} to `true`
* to disable this safeguard.
*/
ignoreUndefinedValues?: boolean;
/**
* If true, then the "jju" library will be used to improve the text formatting.
* Note that this is slightly slower than the native JSON.stringify() implementation.
*/
prettyFormatting?: boolean;
/**
* If specified, this header will be prepended to the start of the file. The header must consist
* of lines prefixed by "//" characters.
* @remarks
* When used with {@link IJsonFileSaveOptions.updateExistingFile}
* or {@link JsonFile.updateString}, the header will ONLY be added for a newly created file.
*/
headerComment?: string;
}
/**
* Options for {@link JsonFile.save} and {@link JsonFile.saveAsync}.
*
* @public
*/
export interface IJsonFileSaveOptions extends IJsonFileStringifyOptions {
/**
* If there is an existing file, and the contents have not changed, then
* don't write anything; this preserves the old timestamp.
*/
onlyIfChanged?: boolean;
/**
* Creates the folder recursively using FileSystem.ensureFolder()
* Defaults to false.
*/
ensureFolderExists?: boolean;
/**
* If true, use the "jju" library to preserve the existing JSON formatting: The file will be loaded
* from the target filename, the new content will be merged in (preserving whitespace and comments),
* and then the file will be overwritten with the merged contents. If the target file does not exist,
* then the file is saved normally.
*/
updateExistingFile?: boolean;
}
/**
* Utilities for reading/writing JSON files.
* @public
*/
export declare class JsonFile {
/**
* @internal
*/
static _formatPathForError: (path: string) => string;
/**
* Loads a JSON file.
*/
static load(jsonFilename: string, options?: IJsonFileParseOptions): JsonObject;
/**
* An async version of {@link JsonFile.load}.
*/
static loadAsync(jsonFilename: string, options?: IJsonFileParseOptions): Promise<JsonObject>;
/**
* Parses a JSON file's contents.
*/
static parseString(jsonContents: string, options?: IJsonFileParseOptions): JsonObject;
/**
* Loads a JSON file and validate its schema.
*/
static loadAndValidate(jsonFilename: string, jsonSchema: JsonSchema, options?: IJsonFileLoadAndValidateOptions): JsonObject;
/**
* An async version of {@link JsonFile.loadAndValidate}.
*/
static loadAndValidateAsync(jsonFilename: string, jsonSchema: JsonSchema, options?: IJsonFileLoadAndValidateOptions): Promise<JsonObject>;
/**
* Loads a JSON file and validate its schema, reporting errors using a callback
* @remarks
* See JsonSchema.validateObjectWithCallback() for more info.
*/
static loadAndValidateWithCallback(jsonFilename: string, jsonSchema: JsonSchema, errorCallback: (errorInfo: IJsonSchemaErrorInfo) => void, options?: IJsonFileLoadAndValidateOptions): JsonObject;
/**
* An async version of {@link JsonFile.loadAndValidateWithCallback}.
*/
static loadAndValidateWithCallbackAsync(jsonFilename: string, jsonSchema: JsonSchema, errorCallback: (errorInfo: IJsonSchemaErrorInfo) => void, options?: IJsonFileLoadAndValidateOptions): Promise<JsonObject>;
/**
* Serializes the specified JSON object to a string buffer.
* @param jsonObject - the object to be serialized
* @param options - other settings that control serialization
* @returns a JSON string, with newlines, and indented with two spaces
*/
static stringify(jsonObject: JsonObject, options?: IJsonFileStringifyOptions): string;
/**
* Serializes the specified JSON object to a string buffer.
* @param previousJson - the previous JSON string, which will be updated
* @param newJsonObject - the object to be serialized
* @param options - other settings that control serialization
* @returns a JSON string, with newlines, and indented with two spaces
*/
static updateString(previousJson: string, newJsonObject: JsonObject, options?: IJsonFileStringifyOptions): string;
/**
* Saves the file to disk. Returns false if nothing was written due to options.onlyIfChanged.
* @param jsonObject - the object to be saved
* @param jsonFilename - the file path to write
* @param options - other settings that control how the file is saved
* @returns false if ISaveJsonFileOptions.onlyIfChanged didn't save anything; true otherwise
*/
static save(jsonObject: JsonObject, jsonFilename: string, options?: IJsonFileSaveOptions): boolean;
/**
* An async version of {@link JsonFile.save}.
*/
static saveAsync(jsonObject: JsonObject, jsonFilename: string, options?: IJsonFileSaveOptions): Promise<boolean>;
/**
* Used to validate a data structure before writing. Reports an error if there
* are any undefined members.
*/
static validateNoUndefinedMembers(jsonObject: JsonObject): void;
private static _validateNoUndefinedMembers;
private static _formatKeyPath;
private static _formatJsonHeaderComment;
private static _buildJjuParseOptions;
}
//# sourceMappingURL=JsonFile.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"JsonFile.d.ts","sourceRoot":"","sources":["../src/JsonFile.ts"],"names":[],"mappings":"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"}
+438
View File
@@ -0,0 +1,438 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.JsonFile = exports.JsonSyntax = void 0;
const os = __importStar(require("os"));
const jju = __importStar(require("jju"));
const Text_1 = require("./Text");
const FileSystem_1 = require("./FileSystem");
/**
* Specifies the variant of JSON syntax to be used.
*
* @public
*/
var JsonSyntax;
(function (JsonSyntax) {
/**
* Specifies the exact RFC 8259 format as implemented by the `JSON.parse()` system API.
* This format was designed for machine generated inputs such as an HTTP payload.
* It is not a recommend choice for human-authored files, because it does not support
* code comments.
*
* @remarks
*
* A well-known quote from Douglas Crockford, the inventor of JSON:
*
* "I removed comments from JSON because I saw people were using them to hold parsing directives,
* a practice which would have destroyed interoperability. I know that the lack of comments makes
* some people sad, but it shouldn't. Suppose you are using JSON to keep configuration files,
* which you would like to annotate. Go ahead and insert all the comments you like.
* Then pipe it through JSMin before handing it to your JSON parser."
*
* @see {@link https://datatracker.ietf.org/doc/html/rfc8259 | RFC 8259}
*/
JsonSyntax["Strict"] = "strict";
/**
* `JsonSyntax.JsonWithComments` is the recommended format for human-authored config files.
* It is a minimal extension to `JsonSyntax.Strict` adding support for code comments
* using `//` and `/*`.
*
* @remarks
*
* VS Code calls this format `jsonc`, but it should not be confused with unrelated file formats
* and libraries that also use the name "JSONC".
*
* To fix VS Code syntax highlighting, add this setting:
* `"files.associations": { "*.json": "jsonc" }`
*
* To fix GitHub syntax highlighting, add this to your `.gitattributes`:
* `*.json linguist-language=JSON-with-Comments`
*/
JsonSyntax["JsonWithComments"] = "jsonWithComments";
/**
* JSON5 is a project that proposes a JSON-like format supplemented with ECMAScript 5.1
* notations for objects, numbers, comments, and more.
*
* @remarks
* Files using this format should use the `.json5` file extension instead of `.json`.
*
* JSON5 has substantial differences from JSON: object keys may be unquoted, trailing commas
* are allowed, and strings may span multiple lines. Whereas {@link JsonSyntax.JsonWithComments} can
* be cheaply converted to standard JSON by stripping comments, parsing JSON5 requires a
* nontrivial algorithm that may not be easily available in some contexts or programming languages.
*
* @see {@link https://json5.org/ | JSON5 project website}
*/
JsonSyntax["Json5"] = "json5";
})(JsonSyntax || (exports.JsonSyntax = JsonSyntax = {}));
const DEFAULT_ENCODING = 'utf8';
/**
* Utilities for reading/writing JSON files.
* @public
*/
class JsonFile {
/**
* Loads a JSON file.
*/
static load(jsonFilename, options) {
try {
const contents = FileSystem_1.FileSystem.readFile(jsonFilename);
const parseOptions = JsonFile._buildJjuParseOptions(options);
return jju.parse(contents, parseOptions);
}
catch (error) {
if (FileSystem_1.FileSystem.isNotExistError(error)) {
throw error;
}
else {
throw new Error(`Error reading "${JsonFile._formatPathForError(jsonFilename)}":` +
os.EOL +
` ${error.message}`);
}
}
}
/**
* An async version of {@link JsonFile.load}.
*/
static async loadAsync(jsonFilename, options) {
try {
const contents = await FileSystem_1.FileSystem.readFileAsync(jsonFilename);
const parseOptions = JsonFile._buildJjuParseOptions(options);
return jju.parse(contents, parseOptions);
}
catch (error) {
if (FileSystem_1.FileSystem.isNotExistError(error)) {
throw error;
}
else {
throw new Error(`Error reading "${JsonFile._formatPathForError(jsonFilename)}":` +
os.EOL +
` ${error.message}`);
}
}
}
/**
* Parses a JSON file's contents.
*/
static parseString(jsonContents, options) {
const parseOptions = JsonFile._buildJjuParseOptions(options);
return jju.parse(jsonContents, parseOptions);
}
/**
* Loads a JSON file and validate its schema.
*/
static loadAndValidate(jsonFilename, jsonSchema, options) {
const jsonObject = JsonFile.load(jsonFilename, options);
jsonSchema.validateObject(jsonObject, jsonFilename, options);
return jsonObject;
}
/**
* An async version of {@link JsonFile.loadAndValidate}.
*/
static async loadAndValidateAsync(jsonFilename, jsonSchema, options) {
const jsonObject = await JsonFile.loadAsync(jsonFilename, options);
jsonSchema.validateObject(jsonObject, jsonFilename, options);
return jsonObject;
}
/**
* Loads a JSON file and validate its schema, reporting errors using a callback
* @remarks
* See JsonSchema.validateObjectWithCallback() for more info.
*/
static loadAndValidateWithCallback(jsonFilename, jsonSchema, errorCallback, options) {
const jsonObject = JsonFile.load(jsonFilename, options);
jsonSchema.validateObjectWithCallback(jsonObject, errorCallback);
return jsonObject;
}
/**
* An async version of {@link JsonFile.loadAndValidateWithCallback}.
*/
static async loadAndValidateWithCallbackAsync(jsonFilename, jsonSchema, errorCallback, options) {
const jsonObject = await JsonFile.loadAsync(jsonFilename, options);
jsonSchema.validateObjectWithCallback(jsonObject, errorCallback);
return jsonObject;
}
/**
* Serializes the specified JSON object to a string buffer.
* @param jsonObject - the object to be serialized
* @param options - other settings that control serialization
* @returns a JSON string, with newlines, and indented with two spaces
*/
static stringify(jsonObject, options) {
return JsonFile.updateString('', jsonObject, options);
}
/**
* Serializes the specified JSON object to a string buffer.
* @param previousJson - the previous JSON string, which will be updated
* @param newJsonObject - the object to be serialized
* @param options - other settings that control serialization
* @returns a JSON string, with newlines, and indented with two spaces
*/
static updateString(previousJson, newJsonObject, options = {}) {
if (!options.ignoreUndefinedValues) {
// Standard handling of `undefined` in JSON stringification is to discard the key.
JsonFile.validateNoUndefinedMembers(newJsonObject);
}
let explicitMode = undefined;
switch (options.jsonSyntax) {
case JsonSyntax.Strict:
explicitMode = 'json';
break;
case JsonSyntax.JsonWithComments:
explicitMode = 'cjson';
break;
case JsonSyntax.Json5:
explicitMode = 'json5';
break;
}
let stringified;
if (previousJson !== '') {
// NOTE: We don't use mode=json here because comments aren't allowed by strict JSON
stringified = jju.update(previousJson, newJsonObject, {
mode: explicitMode !== null && explicitMode !== void 0 ? explicitMode : JsonSyntax.Json5,
indent: 2
});
}
else if (options.prettyFormatting) {
stringified = jju.stringify(newJsonObject, {
mode: explicitMode !== null && explicitMode !== void 0 ? explicitMode : 'json',
indent: 2
});
if (options.headerComment !== undefined) {
stringified = JsonFile._formatJsonHeaderComment(options.headerComment) + stringified;
}
}
else {
stringified = JSON.stringify(newJsonObject, undefined, 2);
if (options.headerComment !== undefined) {
stringified = JsonFile._formatJsonHeaderComment(options.headerComment) + stringified;
}
}
// Add the trailing newline
stringified = Text_1.Text.ensureTrailingNewline(stringified);
if (options.newlineConversion) {
stringified = Text_1.Text.convertTo(stringified, options.newlineConversion);
}
return stringified;
}
/**
* Saves the file to disk. Returns false if nothing was written due to options.onlyIfChanged.
* @param jsonObject - the object to be saved
* @param jsonFilename - the file path to write
* @param options - other settings that control how the file is saved
* @returns false if ISaveJsonFileOptions.onlyIfChanged didn't save anything; true otherwise
*/
static save(jsonObject, jsonFilename, options = {}) {
// Do we need to read the previous file contents?
let oldBuffer = undefined;
if (options.updateExistingFile || options.onlyIfChanged) {
try {
oldBuffer = FileSystem_1.FileSystem.readFileToBuffer(jsonFilename);
}
catch (error) {
if (!FileSystem_1.FileSystem.isNotExistError(error)) {
throw error;
}
}
}
let jsonToUpdate = '';
if (options.updateExistingFile && oldBuffer) {
jsonToUpdate = oldBuffer.toString(DEFAULT_ENCODING);
}
const newJson = JsonFile.updateString(jsonToUpdate, jsonObject, options);
const newBuffer = Buffer.from(newJson, DEFAULT_ENCODING);
if (options.onlyIfChanged) {
// Has the file changed?
if (oldBuffer && Buffer.compare(newBuffer, oldBuffer) === 0) {
// Nothing has changed, so don't touch the file
return false;
}
}
FileSystem_1.FileSystem.writeFile(jsonFilename, newBuffer, {
ensureFolderExists: options.ensureFolderExists
});
// TEST CODE: Used to verify that onlyIfChanged isn't broken by a hidden transformation during saving.
/*
const oldBuffer2: Buffer = FileSystem.readFileToBuffer(jsonFilename);
if (Buffer.compare(buffer, oldBuffer2) !== 0) {
console.log('new:' + buffer.toString('hex'));
console.log('old:' + oldBuffer2.toString('hex'));
throw new Error('onlyIfChanged logic is broken');
}
*/
return true;
}
/**
* An async version of {@link JsonFile.save}.
*/
static async saveAsync(jsonObject, jsonFilename, options = {}) {
// Do we need to read the previous file contents?
let oldBuffer = undefined;
if (options.updateExistingFile || options.onlyIfChanged) {
try {
oldBuffer = await FileSystem_1.FileSystem.readFileToBufferAsync(jsonFilename);
}
catch (error) {
if (!FileSystem_1.FileSystem.isNotExistError(error)) {
throw error;
}
}
}
let jsonToUpdate = '';
if (options.updateExistingFile && oldBuffer) {
jsonToUpdate = oldBuffer.toString(DEFAULT_ENCODING);
}
const newJson = JsonFile.updateString(jsonToUpdate, jsonObject, options);
const newBuffer = Buffer.from(newJson, DEFAULT_ENCODING);
if (options.onlyIfChanged) {
// Has the file changed?
if (oldBuffer && Buffer.compare(newBuffer, oldBuffer) === 0) {
// Nothing has changed, so don't touch the file
return false;
}
}
await FileSystem_1.FileSystem.writeFileAsync(jsonFilename, newBuffer, {
ensureFolderExists: options.ensureFolderExists
});
// TEST CODE: Used to verify that onlyIfChanged isn't broken by a hidden transformation during saving.
/*
const oldBuffer2: Buffer = await FileSystem.readFileToBufferAsync(jsonFilename);
if (Buffer.compare(buffer, oldBuffer2) !== 0) {
console.log('new:' + buffer.toString('hex'));
console.log('old:' + oldBuffer2.toString('hex'));
throw new Error('onlyIfChanged logic is broken');
}
*/
return true;
}
/**
* Used to validate a data structure before writing. Reports an error if there
* are any undefined members.
*/
static validateNoUndefinedMembers(jsonObject) {
return JsonFile._validateNoUndefinedMembers(jsonObject, []);
}
// Private implementation of validateNoUndefinedMembers()
static _validateNoUndefinedMembers(jsonObject, keyPath) {
if (!jsonObject) {
return;
}
if (typeof jsonObject === 'object') {
for (const key of Object.keys(jsonObject)) {
keyPath.push(key);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const value = jsonObject[key];
if (value === undefined) {
const fullPath = JsonFile._formatKeyPath(keyPath);
throw new Error(`The value for ${fullPath} is "undefined" and cannot be serialized as JSON`);
}
JsonFile._validateNoUndefinedMembers(value, keyPath);
keyPath.pop();
}
}
}
// Given this input: ['items', '4', 'syntax', 'parameters', 'string "with" symbols", 'type']
// Return this string: items[4].syntax.parameters["string \"with\" symbols"].type
static _formatKeyPath(keyPath) {
let result = '';
for (const key of keyPath) {
if (/^[0-9]+$/.test(key)) {
// It's an integer, so display like this: parent[123]
result += `[${key}]`;
}
else if (/^[a-z_][a-z_0-9]*$/i.test(key)) {
// It's an alphanumeric identifier, so display like this: parent.name
if (result) {
result += '.';
}
result += `${key}`;
}
else {
// It's a freeform string, so display like this: parent["A path: \"C:\\file\""]
// Convert this: A path: "C:\file"
// To this: A path: \"C:\\file\"
const escapedKey = key
.replace(/[\\]/g, '\\\\') // escape backslashes
.replace(/["]/g, '\\'); // escape quotes
result += `["${escapedKey}"]`;
}
}
return result;
}
static _formatJsonHeaderComment(headerComment) {
if (headerComment === '') {
return '';
}
const lines = headerComment.split('\n');
const result = [];
for (const line of lines) {
if (!/^\s*$/.test(line) && !/^\s*\/\//.test(line)) {
throw new Error('The headerComment lines must be blank or start with the "//" prefix.\n' +
'Invalid line' +
JSON.stringify(line));
}
result.push(Text_1.Text.replaceAll(line, '\r', ''));
}
return lines.join('\n') + '\n';
}
static _buildJjuParseOptions(options = {}) {
const parseOptions = {
reserved_keys: 'replace'
};
switch (options.jsonSyntax) {
case JsonSyntax.Strict:
parseOptions.mode = 'json';
break;
case JsonSyntax.JsonWithComments:
parseOptions.mode = 'cjson';
break;
case JsonSyntax.Json5:
default:
parseOptions.mode = 'json5';
break;
}
return parseOptions;
}
}
exports.JsonFile = JsonFile;
/**
* @internal
*/
JsonFile._formatPathForError = (path) => path;
//# sourceMappingURL=JsonFile.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"JsonFile.js","sourceRoot":"","sources":["../src/JsonFile.ts"],"names":[],"mappings":"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 truncated
+172
View File
@@ -0,0 +1,172 @@
import { type JsonObject } from './JsonFile';
/**
* Specifies the version of json-schema to be validated against.
* https://json-schema.org/specification
* @public
*/
export type JsonSchemaVersion = 'draft-04' | 'draft-07';
/**
* A definition for a custom format to consider during validation.
* @public
*/
export interface IJsonSchemaCustomFormat<T extends string | number> {
/**
* The base JSON type.
*/
type: T extends string ? 'string' : T extends number ? 'number' : never;
/**
* A validation function for the format.
* @param data - The raw field data to validate.
* @returns whether the data is valid according to the format.
*/
validate: (data: T) => boolean;
}
/**
* Callback function arguments for {@link JsonSchema.validateObjectWithCallback}
* @public
*/
export interface IJsonSchemaErrorInfo {
/**
* The ajv error list, formatted as an indented text string.
*/
details: string;
}
/**
* Options for {@link JsonSchema.validateObjectWithCallback}
* @public
*/
export interface IJsonSchemaValidateObjectWithOptions {
/**
* If true, the root-level `$schema` property in a JSON object being validated will be ignored during validation.
* If this is set to `true` and the schema requires a `$schema` property, validation will fail.
*/
ignoreSchemaField?: boolean;
}
/**
* Options for {@link JsonSchema.validateObject}
* @public
*/
export interface IJsonSchemaValidateOptions extends IJsonSchemaValidateObjectWithOptions {
/**
* A custom header that will be used to report schema errors.
* @remarks
* If omitted, the default header is "JSON validation failed:". The error message starts with
* the header, followed by the full input filename, followed by the ajv error list.
* If you wish to customize all aspects of the error message, use JsonFile.loadAndValidateWithCallback()
* or JsonSchema.validateObjectWithCallback().
*/
customErrorHeader?: string;
}
/**
* Options for {@link JsonSchema.fromFile} and {@link JsonSchema.fromLoadedObject}
* @public
*/
export interface IJsonSchemaLoadOptions {
/**
* Other schemas that this schema references, e.g. via the "$ref" directive.
* @remarks
* The tree of dependent schemas may reference the same schema more than once.
* However, if the same schema "$id" is used by two different JsonSchema instances,
* an error will be reported. This means you cannot load the same filename twice
* and use them both together, and you cannot have diamond dependencies on different
* versions of the same schema. Although technically this would be possible to support,
* it normally indicates an error or design problem.
*
* JsonSchema also does not allow circular references between schema dependencies.
*/
dependentSchemas?: JsonSchema[];
/**
* The json-schema version to target for validation.
*
* @defaultValue draft-07
*
* @remarks
* If the a version is not explicitly set, the schema object's `$schema` property
* will be inspected to determine the version. If a `$schema` property is not found
* or does not match an expected URL, the default version will be used.
*/
schemaVersion?: JsonSchemaVersion;
/**
* Any custom formats to consider during validation. Some standard formats are supported
* out-of-the-box (e.g. emails, uris), but additional formats can be defined here. You could
* for example define generic numeric formats (e.g. uint8) or domain-specific formats.
*/
customFormats?: Record<string, IJsonSchemaCustomFormat<string> | IJsonSchemaCustomFormat<number>>;
}
/**
* Options for {@link JsonSchema.fromFile}
* @public
*/
export type IJsonSchemaFromFileOptions = IJsonSchemaLoadOptions;
/**
* Options for {@link JsonSchema.fromLoadedObject}
* @public
*/
export type IJsonSchemaFromObjectOptions = IJsonSchemaLoadOptions;
/**
* Represents a JSON schema that can be used to validate JSON data files loaded by the JsonFile class.
* @remarks
* The schema itself is normally loaded and compiled later, only if it is actually required to validate
* an input. To avoid schema errors at runtime, it's recommended to create a unit test that calls
* JsonSchema.ensureCompiled() for each of your schema objects.
*
* @public
*/
export declare class JsonSchema {
private _dependentSchemas;
private _filename;
private _validator;
private _schemaObject;
private _schemaVersion;
private _customFormats;
private constructor();
/**
* Registers a JsonSchema that will be loaded from a file on disk.
* @remarks
* NOTE: An error occurs if the file does not exist; however, the file itself is not loaded or validated
* until it the schema is actually used.
*/
static fromFile(filename: string, options?: IJsonSchemaFromFileOptions): JsonSchema;
/**
* Registers a JsonSchema that will be loaded from an object.
*/
static fromLoadedObject(schemaObject: JsonObject, options?: IJsonSchemaFromObjectOptions): JsonSchema;
private static _collectDependentSchemas;
/**
* Used to nicely format the ZSchema error tree.
*/
private static _formatErrorDetails;
/**
* Used by _formatErrorDetails.
*/
private static _formatErrorDetailsHelper;
/**
* Returns a short name for this schema, for use in error messages.
* @remarks
* If the schema was loaded from a file, then the base filename is used. Otherwise, the "$id"
* field is used if available.
*/
get shortName(): string;
/**
* If not already done, this loads the schema from disk and compiles it.
* @remarks
* Any dependencies will be compiled as well.
*/
ensureCompiled(): void;
/**
* Validates the specified JSON object against this JSON schema. If the validation fails,
* an exception will be thrown.
* @param jsonObject - The JSON data to be validated
* @param filenameForErrors - The filename that the JSON data was available, or an empty string
* if not applicable
* @param options - Other options that control the validation
*/
validateObject(jsonObject: JsonObject, filenameForErrors: string, options?: IJsonSchemaValidateOptions): void;
/**
* Validates the specified JSON object against this JSON schema. If the validation fails,
* a callback is called for each validation error.
*/
validateObjectWithCallback(jsonObject: JsonObject, errorCallback: (errorInfo: IJsonSchemaErrorInfo) => void, options?: IJsonSchemaValidateObjectWithOptions): void;
private _ensureLoaded;
}
//# sourceMappingURL=JsonSchema.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"JsonSchema.d.ts","sourceRoot":"","sources":["../src/JsonSchema.ts"],"names":[],"mappings":"AAOA,OAAO,EAAY,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAavD;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,UAAU,CAAC;AAExD;;;GAGG;AACH,MAAM,WAAW,uBAAuB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IAChE;;OAEG;IACH,IAAI,EAAE,CAAC,SAAS,MAAM,GAAG,QAAQ,GAAG,CAAC,SAAS,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IAExE;;;;OAIG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,oCAAoC;IACnD;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,0BAA2B,SAAQ,oCAAoC;IACtF;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,EAAE,UAAU,EAAE,CAAC;IAEhC;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAElC;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,MAAM,CAAC,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC,CAAC;CACnG;AAED;;;GAGG;AACH,MAAM,MAAM,0BAA0B,GAAG,sBAAsB,CAAC;AAEhE;;;GAGG;AACH,MAAM,MAAM,4BAA4B,GAAG,sBAAsB,CAAC;AAoBlE;;;;;;;;GAQG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,UAAU,CAA2C;IAC7D,OAAO,CAAC,aAAa,CAAqC;IAC1D,OAAO,CAAC,cAAc,CAA4C;IAClE,OAAO,CAAC,cAAc,CAEI;IAE1B,OAAO;IAEP;;;;;OAKG;WACW,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,0BAA0B,GAAG,UAAU;IAmB1F;;OAEG;WACW,gBAAgB,CAC5B,YAAY,EAAE,UAAU,EACxB,OAAO,CAAC,EAAE,4BAA4B,GACrC,UAAU;IAab,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAuCvC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAIlC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,yBAAyB;IAiBxC;;;;;OAKG;IACH,IAAW,SAAS,IAAI,MAAM,CAc7B;IAED;;;;OAIG;IACI,cAAc,IAAI,IAAI;IA2D7B;;;;;;;OAOG;IACI,cAAc,CACnB,UAAU,EAAE,UAAU,EACtB,iBAAiB,EAAE,MAAM,EACzB,OAAO,CAAC,EAAE,0BAA0B,GACnC,IAAI;IAYP;;;OAGG;IACI,0BAA0B,CAC/B,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,CAAC,SAAS,EAAE,oBAAoB,KAAK,IAAI,EACxD,OAAO,CAAC,EAAE,oCAAoC,GAC7C,IAAI;IAsBP,OAAO,CAAC,aAAa;CAMtB"}
+287
View File
@@ -0,0 +1,287 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.JsonSchema = void 0;
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const FileSystem_1 = require("./FileSystem");
const JsonFile_1 = require("./JsonFile");
const ajv_1 = __importDefault(require("ajv"));
const ajv_draft_04_1 = __importDefault(require("ajv-draft-04"));
const ajv_formats_1 = __importDefault(require("ajv-formats"));
const JSON_SCHEMA_URL_PREFIX_BY_JSON_SCHEMA_VERSION = new Map([
['draft-04', 'http://json-schema.org/draft-04/schema'],
['draft-07', 'http://json-schema.org/draft-07/schema']
]);
/**
* Helper function to determine the json-schema version to target for validation.
*/
function _inferJsonSchemaVersion({ $schema }) {
if ($schema) {
for (const [jsonSchemaVersion, urlPrefix] of JSON_SCHEMA_URL_PREFIX_BY_JSON_SCHEMA_VERSION) {
if ($schema.startsWith(urlPrefix)) {
return jsonSchemaVersion;
}
}
}
}
/**
* Represents a JSON schema that can be used to validate JSON data files loaded by the JsonFile class.
* @remarks
* The schema itself is normally loaded and compiled later, only if it is actually required to validate
* an input. To avoid schema errors at runtime, it's recommended to create a unit test that calls
* JsonSchema.ensureCompiled() for each of your schema objects.
*
* @public
*/
class JsonSchema {
constructor() {
this._dependentSchemas = [];
this._filename = '';
this._validator = undefined;
this._schemaObject = undefined;
this._schemaVersion = undefined;
this._customFormats = undefined;
}
/**
* Registers a JsonSchema that will be loaded from a file on disk.
* @remarks
* NOTE: An error occurs if the file does not exist; however, the file itself is not loaded or validated
* until it the schema is actually used.
*/
static fromFile(filename, options) {
// This is a quick and inexpensive test to avoid the catch the most common errors early.
// Full validation will happen later in JsonSchema.compile().
if (!FileSystem_1.FileSystem.exists(filename)) {
throw new Error('Schema file not found: ' + filename);
}
const schema = new JsonSchema();
schema._filename = filename;
if (options) {
schema._dependentSchemas = options.dependentSchemas || [];
schema._schemaVersion = options.schemaVersion;
schema._customFormats = options.customFormats;
}
return schema;
}
/**
* Registers a JsonSchema that will be loaded from an object.
*/
static fromLoadedObject(schemaObject, options) {
const schema = new JsonSchema();
schema._schemaObject = schemaObject;
if (options) {
schema._dependentSchemas = options.dependentSchemas || [];
schema._schemaVersion = options.schemaVersion;
schema._customFormats = options.customFormats;
}
return schema;
}
static _collectDependentSchemas(collectedSchemas, dependentSchemas, seenObjects, seenIds) {
for (const dependentSchema of dependentSchemas) {
// It's okay for the same schema to appear multiple times in the tree, but we only process it once
if (seenObjects.has(dependentSchema)) {
continue;
}
seenObjects.add(dependentSchema);
const schemaId = dependentSchema._ensureLoaded();
if (schemaId === '') {
throw new Error(`This schema ${dependentSchema.shortName} cannot be referenced` +
' because is missing the "id" (draft-04) or "$id" field');
}
if (seenIds.has(schemaId)) {
throw new Error(`This schema ${dependentSchema.shortName} has the same "id" (draft-04) or "$id" as another schema in this set`);
}
seenIds.add(schemaId);
collectedSchemas.push(dependentSchema);
JsonSchema._collectDependentSchemas(collectedSchemas, dependentSchema._dependentSchemas, seenObjects, seenIds);
}
}
/**
* Used to nicely format the ZSchema error tree.
*/
static _formatErrorDetails(errorDetails) {
return JsonSchema._formatErrorDetailsHelper(errorDetails, '', '');
}
/**
* Used by _formatErrorDetails.
*/
static _formatErrorDetailsHelper(errorDetails, indent, buffer) {
var _a, _b;
for (const errorDetail of errorDetails) {
buffer += os.EOL + indent + `Error: #${errorDetail.instancePath}`;
buffer += os.EOL + indent + ` ${errorDetail.message}`;
if ((_a = errorDetail.params) === null || _a === void 0 ? void 0 : _a.additionalProperty) {
buffer += `: ${(_b = errorDetail.params) === null || _b === void 0 ? void 0 : _b.additionalProperty}`;
}
}
return buffer;
}
/**
* Returns a short name for this schema, for use in error messages.
* @remarks
* If the schema was loaded from a file, then the base filename is used. Otherwise, the "$id"
* field is used if available.
*/
get shortName() {
if (!this._filename) {
if (this._schemaObject) {
const schemaWithId = this._schemaObject;
if (schemaWithId.id) {
return schemaWithId.id;
}
else if (schemaWithId.$id) {
return schemaWithId.$id;
}
}
return '(anonymous schema)';
}
else {
return path.basename(this._filename);
}
}
/**
* If not already done, this loads the schema from disk and compiles it.
* @remarks
* Any dependencies will be compiled as well.
*/
ensureCompiled() {
var _a;
this._ensureLoaded();
if (!this._validator) {
const targetSchemaVersion = (_a = this._schemaVersion) !== null && _a !== void 0 ? _a : _inferJsonSchemaVersion(this._schemaObject);
const validatorOptions = {
strictSchema: true,
allowUnionTypes: true
};
let validator;
// Keep legacy support for older draft-04 schema
switch (targetSchemaVersion) {
case 'draft-04': {
validator = new ajv_draft_04_1.default(validatorOptions);
break;
}
case 'draft-07':
default: {
validator = new ajv_1.default(validatorOptions);
break;
}
}
// Enable json-schema format validation
// https://ajv.js.org/packages/ajv-formats.html
(0, ajv_formats_1.default)(validator);
if (this._customFormats) {
for (const [name, format] of Object.entries(this._customFormats)) {
validator.addFormat(name, Object.assign(Object.assign({}, format), { async: false }));
}
}
const collectedSchemas = [];
const seenObjects = new Set();
const seenIds = new Set();
JsonSchema._collectDependentSchemas(collectedSchemas, this._dependentSchemas, seenObjects, seenIds);
// Validate each schema in order. We specifically do not supply them all together, because we want
// to make sure that circular references will fail to validate.
for (const collectedSchema of collectedSchemas) {
validator.validateSchema(collectedSchema._schemaObject);
if (validator.errors && validator.errors.length > 0) {
throw new Error(`Failed to validate schema "${collectedSchema.shortName}":` +
os.EOL +
JsonSchema._formatErrorDetails(validator.errors));
}
validator.addSchema(collectedSchema._schemaObject);
}
this._validator = validator.compile(this._schemaObject);
}
}
/**
* Validates the specified JSON object against this JSON schema. If the validation fails,
* an exception will be thrown.
* @param jsonObject - The JSON data to be validated
* @param filenameForErrors - The filename that the JSON data was available, or an empty string
* if not applicable
* @param options - Other options that control the validation
*/
validateObject(jsonObject, filenameForErrors, options) {
this.validateObjectWithCallback(jsonObject, (errorInfo) => {
var _a;
const prefix = (_a = options === null || options === void 0 ? void 0 : options.customErrorHeader) !== null && _a !== void 0 ? _a : 'JSON validation failed:';
throw new Error(prefix + os.EOL + filenameForErrors + os.EOL + errorInfo.details);
}, options);
}
/**
* Validates the specified JSON object against this JSON schema. If the validation fails,
* a callback is called for each validation error.
*/
validateObjectWithCallback(jsonObject, errorCallback, options) {
this.ensureCompiled();
if (options === null || options === void 0 ? void 0 : options.ignoreSchemaField) {
const {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
$schema } = jsonObject, remainder = __rest(jsonObject, ["$schema"]);
jsonObject = remainder;
}
if (this._validator && !this._validator(jsonObject)) {
const errorDetails = JsonSchema._formatErrorDetails(this._validator.errors);
const args = {
details: errorDetails
};
errorCallback(args);
}
}
_ensureLoaded() {
if (!this._schemaObject) {
this._schemaObject = JsonFile_1.JsonFile.load(this._filename);
}
return this._schemaObject.id || this._schemaObject.$id || '';
}
}
exports.JsonSchema = JsonSchema;
//# sourceMappingURL=JsonSchema.js.map
@@ -0,0 +1 @@
{"version":3,"file":"JsonSchema.js","sourceRoot":"","sources":["../src/JsonSchema.ts"],"names":[],"mappings":"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 truncated
@@ -0,0 +1,24 @@
/**
* Callback used by {@link LegacyAdapters}.
* @public
*/
export type LegacyCallback<TResult, TError> = (error: TError | null | undefined, result: TResult) => void;
/**
* Helper functions used when interacting with APIs that do not follow modern coding practices.
* @public
*/
export declare class LegacyAdapters {
/**
* This function wraps a function with a callback in a promise.
*/
static convertCallbackToPromise<TResult, TError>(fn: (cb: LegacyCallback<TResult, TError>) => void): Promise<TResult>;
static convertCallbackToPromise<TResult, TError, TArg1>(fn: (arg1: TArg1, cb: LegacyCallback<TResult, TError>) => void, arg1: TArg1): Promise<TResult>;
static convertCallbackToPromise<TResult, TError, TArg1, TArg2>(fn: (arg1: TArg1, arg2: TArg2, cb: LegacyCallback<TResult, TError>) => void, arg1: TArg1, arg2: TArg2): Promise<TResult>;
static convertCallbackToPromise<TResult, TError, TArg1, TArg2, TArg3>(fn: (arg1: TArg1, arg2: TArg2, arg3: TArg3, cb: LegacyCallback<TResult, TError>) => void, arg1: TArg1, arg2: TArg2, arg3: TArg3): Promise<TResult>;
static convertCallbackToPromise<TResult, TError, TArg1, TArg2, TArg3, TArg4>(fn: (arg1: TArg1, arg2: TArg2, arg3: TArg3, arg4: TArg4, cb: LegacyCallback<TResult, TError>) => void, arg1: TArg1, arg2: TArg2, arg3: TArg3, arg4: TArg4): Promise<TResult>;
/**
* Normalizes an object into an `Error` object.
*/
static scrubError(error: Error | string | any): Error;
}
//# sourceMappingURL=LegacyAdapters.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"LegacyAdapters.d.ts","sourceRoot":"","sources":["../src/LegacyAdapters.ts"],"names":[],"mappings":"AAGA;;;GAGG;AAEH,MAAM,MAAM,cAAc,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;AAE1G;;;GAGG;AACH,qBAAa,cAAc;IACzB;;OAEG;WACW,wBAAwB,CAAC,OAAO,EAAE,MAAM,EACpD,EAAE,EAAE,CAAC,EAAE,EAAE,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,IAAI,GAChD,OAAO,CAAC,OAAO,CAAC;WACL,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAC3D,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,IAAI,EAC9D,IAAI,EAAE,KAAK,GACV,OAAO,CAAC,OAAO,CAAC;WACL,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAClE,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,IAAI,EAC3E,IAAI,EAAE,KAAK,EACX,IAAI,EAAE,KAAK,GACV,OAAO,CAAC,OAAO,CAAC;WACL,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EACzE,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,IAAI,EACxF,IAAI,EAAE,KAAK,EACX,IAAI,EAAE,KAAK,EACX,IAAI,EAAE,KAAK,GACV,OAAO,CAAC,OAAO,CAAC;WACL,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAChF,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,IAAI,EACrG,IAAI,EAAE,KAAK,EACX,IAAI,EAAE,KAAK,EACX,IAAI,EAAE,KAAK,EACX,IAAI,EAAE,KAAK,GACV,OAAO,CAAC,OAAO,CAAC;IAyCnB;;OAEG;WAEW,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,GAAG,GAAG,KAAK;CAW7D"}
+62
View File
@@ -0,0 +1,62 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.LegacyAdapters = void 0;
/**
* Helper functions used when interacting with APIs that do not follow modern coding practices.
* @public
*/
class LegacyAdapters {
static convertCallbackToPromise(fn, arg1, arg2, arg3, arg4) {
return new Promise((resolve, reject) => {
const cb = (error, result) => {
if (error) {
reject(LegacyAdapters.scrubError(error));
}
else {
resolve(result);
}
};
try {
if (arg1 !== undefined && arg2 !== undefined && arg3 !== undefined && arg4 !== undefined) {
fn(arg1, arg2, arg3, arg4, cb);
}
else if (arg1 !== undefined && arg2 !== undefined && arg3 !== undefined) {
fn(arg1, arg2, arg3, cb);
}
else if (arg1 !== undefined && arg2 !== undefined) {
fn(arg1, arg2, cb);
}
else if (arg1 !== undefined) {
fn(arg1, cb);
}
else {
fn(cb);
}
}
catch (e) {
reject(e);
}
});
}
/**
* Normalizes an object into an `Error` object.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static scrubError(error) {
if (error instanceof Error) {
return error;
}
else if (typeof error === 'string') {
return new Error(error);
}
else {
const errorObject = new Error('An error occurred.');
errorObject.errorData = error; // eslint-disable-line @typescript-eslint/no-explicit-any
return errorObject;
}
}
}
exports.LegacyAdapters = LegacyAdapters;
//# sourceMappingURL=LegacyAdapters.js.map
@@ -0,0 +1 @@
{"version":3,"file":"LegacyAdapters.js","sourceRoot":"","sources":["../src/LegacyAdapters.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAS3D;;;GAGG;AACH,MAAa,cAAc;IA6BlB,MAAM,CAAC,wBAAwB,CACpC,EAMS,EACT,IAAY,EACZ,IAAY,EACZ,IAAY,EACZ,IAAY;QAEZ,OAAO,IAAI,OAAO,CAAC,CAAC,OAAkC,EAAE,MAA8B,EAAE,EAAE;YACxF,MAAM,EAAE,GAAoC,CAAC,KAAgC,EAAE,MAAe,EAAE,EAAE;gBAChG,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC3C,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,MAAM,CAAC,CAAC;gBAClB,CAAC;YACH,CAAC,CAAC;YAEF,IAAI,CAAC;gBACH,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;oBACzF,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;gBACjC,CAAC;qBAAM,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC1E,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC3B,CAAC;qBAAM,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;oBACpD,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;gBACrB,CAAC;qBAAM,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;oBAC9B,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACf,CAAC;qBAAM,CAAC;oBACN,EAAE,CAAC,EAAE,CAAC,CAAC;gBACT,CAAC;YACH,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,MAAM,CAAC,CAAU,CAAC,CAAC;YACrB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,8DAA8D;IACvD,MAAM,CAAC,UAAU,CAAC,KAA2B;QAClD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,OAAO,KAAK,CAAC;QACf,CAAC;aAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACrC,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,MAAM,WAAW,GAAU,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;YAC1D,WAAmB,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC,yDAAyD;YACjG,OAAO,WAAW,CAAC;QACrB,CAAC;IACH,CAAC;CACF;AApFD,wCAoFC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n/**\n * Callback used by {@link LegacyAdapters}.\n * @public\n */\n// eslint-disable-next-line @rushstack/no-new-null\nexport type LegacyCallback<TResult, TError> = (error: TError | null | undefined, result: TResult) => void;\n\n/**\n * Helper functions used when interacting with APIs that do not follow modern coding practices.\n * @public\n */\nexport class LegacyAdapters {\n /**\n * This function wraps a function with a callback in a promise.\n */\n public static convertCallbackToPromise<TResult, TError>(\n fn: (cb: LegacyCallback<TResult, TError>) => void\n ): Promise<TResult>;\n public static convertCallbackToPromise<TResult, TError, TArg1>(\n fn: (arg1: TArg1, cb: LegacyCallback<TResult, TError>) => void,\n arg1: TArg1\n ): Promise<TResult>;\n public static convertCallbackToPromise<TResult, TError, TArg1, TArg2>(\n fn: (arg1: TArg1, arg2: TArg2, cb: LegacyCallback<TResult, TError>) => void,\n arg1: TArg1,\n arg2: TArg2\n ): Promise<TResult>;\n public static convertCallbackToPromise<TResult, TError, TArg1, TArg2, TArg3>(\n fn: (arg1: TArg1, arg2: TArg2, arg3: TArg3, cb: LegacyCallback<TResult, TError>) => void,\n arg1: TArg1,\n arg2: TArg2,\n arg3: TArg3\n ): Promise<TResult>;\n public static convertCallbackToPromise<TResult, TError, TArg1, TArg2, TArg3, TArg4>(\n fn: (arg1: TArg1, arg2: TArg2, arg3: TArg3, arg4: TArg4, cb: LegacyCallback<TResult, TError>) => void,\n arg1: TArg1,\n arg2: TArg2,\n arg3: TArg3,\n arg4: TArg4\n ): Promise<TResult>;\n public static convertCallbackToPromise<TResult, TError, TArg1, TArg2, TArg3, TArg4>(\n fn: (\n a: TArg1 | LegacyCallback<TResult, TError>,\n b?: TArg2 | LegacyCallback<TResult, TError>,\n c?: TArg3 | LegacyCallback<TResult, TError>,\n d?: TArg4 | LegacyCallback<TResult, TError>,\n e?: TArg4 | LegacyCallback<TResult, TError>\n ) => void,\n arg1?: TArg1,\n arg2?: TArg2,\n arg3?: TArg3,\n arg4?: TArg4\n ): Promise<TResult> {\n return new Promise((resolve: (result: TResult) => void, reject: (error: Error) => void) => {\n const cb: LegacyCallback<TResult, TError> = (error: TError | null | undefined, result: TResult) => {\n if (error) {\n reject(LegacyAdapters.scrubError(error));\n } else {\n resolve(result);\n }\n };\n\n try {\n if (arg1 !== undefined && arg2 !== undefined && arg3 !== undefined && arg4 !== undefined) {\n fn(arg1, arg2, arg3, arg4, cb);\n } else if (arg1 !== undefined && arg2 !== undefined && arg3 !== undefined) {\n fn(arg1, arg2, arg3, cb);\n } else if (arg1 !== undefined && arg2 !== undefined) {\n fn(arg1, arg2, cb);\n } else if (arg1 !== undefined) {\n fn(arg1, cb);\n } else {\n fn(cb);\n }\n } catch (e) {\n reject(e as Error);\n }\n });\n }\n\n /**\n * Normalizes an object into an `Error` object.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n public static scrubError(error: Error | string | any): Error {\n if (error instanceof Error) {\n return error;\n } else if (typeof erLine truncated
+94
View File
@@ -0,0 +1,94 @@
/**
* Parses the process start time from the contents of a linux /proc/[pid]/stat file.
* @param stat - The contents of a linux /proc/[pid]/stat file.
* @returns The process start time in jiffies, or undefined if stat has an unexpected format.
*/
export declare function getProcessStartTimeFromProcStat(stat: string): string | undefined;
/**
* Helper function that is exported for unit tests only.
* Returns undefined if the process doesn't exist with that pid.
*/
export declare function getProcessStartTime(pid: number): string | undefined;
/**
* The `LockFile` implements a file-based mutex for synchronizing access to a shared resource
* between multiple Node.js processes. It is not recommended for synchronization solely within
* a single Node.js process.
* @remarks
* The implementation works on Windows, Mac, and Linux without requiring any native helpers.
* On non-Windows systems, the algorithm requires access to the `ps` shell command. On Linux,
* it requires access the `/proc/${pidString}/stat` filesystem.
* @public
*/
export declare class LockFile {
private static _getStartTime;
private _fileWriter;
private _filePath;
private _dirtyWhenAcquired;
private constructor();
/**
* Returns the path of the lockfile that will be created when a lock is successfully acquired.
* @param resourceFolder - The folder where the lock file will be created
* @param resourceName - An alphanumeric name that describes the resource being locked. This will become
* the filename of the temporary file created to manage the lock.
* @param pid - The PID for the current Node.js process (`process.pid`), which is used by the locking algorithm.
*/
static getLockFilePath(resourceFolder: string, resourceName: string, pid?: number): string;
/**
* Attempts to create a lockfile with the given filePath.
* @param resourceFolder - The folder where the lock file will be created
* @param resourceName - An alphanumeric name that describes the resource being locked. This will become
* the filename of the temporary file created to manage the lock.
* @returns If successful, returns a `LockFile` instance. If unable to get a lock, returns `undefined`.
*/
static tryAcquire(resourceFolder: string, resourceName: string): LockFile | undefined;
/**
* @deprecated Use {@link LockFile.acquireAsync} instead.
*/
static acquire(resourceFolder: string, resourceName: string, maxWaitMs?: number): Promise<LockFile>;
/**
* Attempts to create the lockfile. Will continue to loop at every 100ms until the lock becomes available
* or the maxWaitMs is surpassed.
*
* @remarks
* This function is subject to starvation, whereby it does not ensure that the process that has been
* waiting the longest to acquire the lock will get it first. This means that a process could theoretically
* wait for the lock forever, while other processes skipped it in line and acquired the lock first.
*
* @param resourceFolder - The folder where the lock file will be created
* @param resourceName - An alphanumeric name that describes the resource being locked. This will become
* the filename of the temporary file created to manage the lock.
* @param maxWaitMs - The maximum number of milliseconds to wait for the lock before reporting an error
*/
static acquireAsync(resourceFolder: string, resourceName: string, maxWaitMs?: number): Promise<LockFile>;
private static _tryAcquireInner;
/**
* Attempts to acquire the lock on a Linux or OSX machine
*/
private static _tryAcquireMacOrLinux;
/**
* Attempts to acquire the lock using Windows
* This algorithm is much simpler since we can rely on the operating system
*/
private static _tryAcquireWindows;
/**
* Unlocks a file and optionally removes it from disk.
* This can only be called once.
*
* @param deleteFile - Whether to delete the lockfile from disk. Defaults to true.
*/
release(deleteFile?: boolean): void;
/**
* Returns the initial state of the lock.
* This can be used to detect if the previous process was terminated before releasing the resource.
*/
get dirtyWhenAcquired(): boolean;
/**
* Returns the absolute path to the lockfile
*/
get filePath(): string;
/**
* Returns true if this lock is currently being held.
*/
get isReleased(): boolean;
}
//# sourceMappingURL=LockFile.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"LockFile.d.ts","sourceRoot":"","sources":["../src/LockFile.ts"],"names":[],"mappings":"AAmBA;;;;GAIG;AACH,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAsChF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAoEnE;AAMD;;;;;;;;;GASG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,MAAM,CAAC,aAAa,CAA4D;IAExF,OAAO,CAAC,WAAW,CAAyB;IAC5C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,kBAAkB,CAAU;IAEpC,OAAO;IAQP;;;;;;OAMG;WACW,eAAe,CAC3B,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,GAAG,GAAE,MAAoB,GACxB,MAAM;IAwBT;;;;;;OAMG;WACW,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAM5F;;OAEG;WACW,OAAO,CAAC,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAI1G;;;;;;;;;;;;;OAaG;WACiB,YAAY,CAC9B,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,QAAQ,CAAC;IA0BpB,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAuB/B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,qBAAqB;IA4IpC;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAuCjC;;;;;OAKG;IACI,OAAO,CAAC,UAAU,GAAE,OAAc,GAAG,IAAI;IAehD;;;OAGG;IACH,IAAW,iBAAiB,IAAI,OAAO,CAEtC;IAED;;OAEG;IACH,IAAW,QAAQ,IAAI,MAAM,CAE5B;IAED;;OAEG;IACH,IAAW,UAAU,IAAI,OAAO,CAE/B;CACF"}
+462
View File
@@ -0,0 +1,462 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.LockFile = void 0;
exports.getProcessStartTimeFromProcStat = getProcessStartTimeFromProcStat;
exports.getProcessStartTime = getProcessStartTime;
const path = __importStar(require("path"));
const child_process = __importStar(require("child_process"));
const FileSystem_1 = require("./FileSystem");
const FileWriter_1 = require("./FileWriter");
const Async_1 = require("./Async");
/**
* http://man7.org/linux/man-pages/man5/proc.5.html
* (22) starttime %llu
* The time the process started after system boot. In kernels before Linux 2.6, this value was
* expressed in jiffies. Since Linux 2.6, the value is expressed in clock ticks (divide by
* sysconf(_SC_CLK_TCK)).
* The format for this field was %lu before Linux 2.6.
*/
const procStatStartTimePos = 22;
/**
* Parses the process start time from the contents of a linux /proc/[pid]/stat file.
* @param stat - The contents of a linux /proc/[pid]/stat file.
* @returns The process start time in jiffies, or undefined if stat has an unexpected format.
*/
function getProcessStartTimeFromProcStat(stat) {
// Parse the value at position procStatStartTimePos.
// We cannot just split stat on spaces, because value 2 may contain spaces.
// For example, when running the following Shell commands:
// > cp "$(which bash)" ./'bash 2)('
// > ./'bash 2)(' -c 'OWNPID=$BASHPID;cat /proc/$OWNPID/stat'
// 59389 (bash 2)() S 59358 59389 59358 34818 59389 4202496 329 0 0 0 0 0 0 0 20 0 1 0
// > rm -rf ./'bash 2)('
// The output shows a stat file such that value 2 contains spaces.
// To still umambiguously parse such output we assume no values after the second ends with a right parenthesis...
// trimRight to remove the trailing line terminator.
let values = stat.trimRight().split(' ');
let i = values.length - 1;
while (i >= 0 &&
// charAt returns an empty string if the index is out of bounds.
values[i].charAt(values[i].length - 1) !== ')') {
i -= 1;
}
// i is the index of the last part of the second value (but i need not be 1).
if (i < 1) {
// Format of stat has changed.
return undefined;
}
const value2 = values.slice(1, i + 1).join(' ');
values = [values[0], value2].concat(values.slice(i + 1));
if (values.length < procStatStartTimePos) {
// Older version of linux, or non-standard configuration of linux.
return undefined;
}
const startTimeJiffies = values[procStatStartTimePos - 1];
// In theory, the representations of start time returned by `cat /proc/[pid]/stat` and `ps -o lstart` can change
// while the system is running, but we assume this does not happen.
// So the caller can safely use this value as part of a unique process id (on the machine, without comparing
// across reboots).
return startTimeJiffies;
}
/**
* Helper function that is exported for unit tests only.
* Returns undefined if the process doesn't exist with that pid.
*/
function getProcessStartTime(pid) {
const pidString = pid.toString();
if (pid < 0 || pidString.indexOf('e') >= 0 || pidString.indexOf('E') >= 0) {
throw new Error(`"pid" is negative or too large`);
}
let args;
if (process.platform === 'darwin') {
args = [`-p ${pidString}`, '-o lstart'];
}
else if (process.platform === 'linux') {
args = ['-p', pidString, '-o', 'lstart'];
}
else {
throw new Error(`Unsupported system: ${process.platform}`);
}
const psResult = child_process.spawnSync('ps', args, {
encoding: 'utf8'
});
const psStdout = psResult.stdout;
// If no process with PID pid exists then the exit code is non-zero on linux but stdout is not empty.
// But if no process exists we do not want to fall back on /proc/*/stat to determine the process
// start time, so we we additionally test for !psStdout. NOTE: !psStdout evaluates to true if
// zero bytes are written to stdout.
if (psResult.status !== 0 && !psStdout && process.platform === 'linux') {
// Try to read /proc/[pid]/stat and get the value at position procStatStartTimePos.
let stat;
try {
stat = FileSystem_1.FileSystem.readFile(`/proc/${pidString}/stat`);
}
catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
// Either no process with PID pid exists, or this version/configuration of linux is non-standard.
// We assume the former.
return undefined;
}
if (stat !== undefined) {
const startTimeJiffies = getProcessStartTimeFromProcStat(stat);
if (startTimeJiffies === undefined) {
throw new Error(`Could not retrieve the start time of process ${pidString} from the OS because the ` +
`contents of /proc/${pidString}/stat have an unexpected format`);
}
return startTimeJiffies;
}
}
// there was an error executing ps (zero bytes were written to stdout).
if (!psStdout) {
throw new Error(`Unexpected output from "ps" command`);
}
const psSplit = psStdout.split('\n');
// successfully able to run "ps", but no process was found
if (psSplit[1] === '') {
return undefined;
}
if (psSplit[1]) {
const trimmed = psSplit[1].trim();
if (trimmed.length > 10) {
return trimmed;
}
}
throw new Error(`Unexpected output from the "ps" command`);
}
// A set of locks that currently exist in the current process, to be used when
// multiple locks are acquired in the same process.
const IN_PROC_LOCKS = new Set();
/**
* The `LockFile` implements a file-based mutex for synchronizing access to a shared resource
* between multiple Node.js processes. It is not recommended for synchronization solely within
* a single Node.js process.
* @remarks
* The implementation works on Windows, Mac, and Linux without requiring any native helpers.
* On non-Windows systems, the algorithm requires access to the `ps` shell command. On Linux,
* it requires access the `/proc/${pidString}/stat` filesystem.
* @public
*/
class LockFile {
constructor(fileWriter, filePath, dirtyWhenAcquired) {
this._fileWriter = fileWriter;
this._filePath = filePath;
this._dirtyWhenAcquired = dirtyWhenAcquired;
IN_PROC_LOCKS.add(filePath);
}
/**
* Returns the path of the lockfile that will be created when a lock is successfully acquired.
* @param resourceFolder - The folder where the lock file will be created
* @param resourceName - An alphanumeric name that describes the resource being locked. This will become
* the filename of the temporary file created to manage the lock.
* @param pid - The PID for the current Node.js process (`process.pid`), which is used by the locking algorithm.
*/
static getLockFilePath(resourceFolder, resourceName, pid = process.pid) {
if (!resourceName.match(/^[a-zA-Z0-9][a-zA-Z0-9-.]+[a-zA-Z0-9]$/)) {
throw new Error(`The resource name "${resourceName}" is invalid.` +
` It must be an alphanumeric string with only "-" or "." It must start and end with an alphanumeric character.`);
}
switch (process.platform) {
case 'win32': {
return path.resolve(resourceFolder, `${resourceName}.lock`);
}
case 'linux':
case 'darwin': {
return path.resolve(resourceFolder, `${resourceName}#${pid}.lock`);
}
default: {
throw new Error(`File locking not implemented for platform: "${process.platform}"`);
}
}
}
/**
* Attempts to create a lockfile with the given filePath.
* @param resourceFolder - The folder where the lock file will be created
* @param resourceName - An alphanumeric name that describes the resource being locked. This will become
* the filename of the temporary file created to manage the lock.
* @returns If successful, returns a `LockFile` instance. If unable to get a lock, returns `undefined`.
*/
static tryAcquire(resourceFolder, resourceName) {
FileSystem_1.FileSystem.ensureFolder(resourceFolder);
const lockFilePath = LockFile.getLockFilePath(resourceFolder, resourceName);
return LockFile._tryAcquireInner(resourceFolder, resourceName, lockFilePath);
}
/**
* @deprecated Use {@link LockFile.acquireAsync} instead.
*/
static acquire(resourceFolder, resourceName, maxWaitMs) {
return LockFile.acquireAsync(resourceFolder, resourceName, maxWaitMs);
}
/**
* Attempts to create the lockfile. Will continue to loop at every 100ms until the lock becomes available
* or the maxWaitMs is surpassed.
*
* @remarks
* This function is subject to starvation, whereby it does not ensure that the process that has been
* waiting the longest to acquire the lock will get it first. This means that a process could theoretically
* wait for the lock forever, while other processes skipped it in line and acquired the lock first.
*
* @param resourceFolder - The folder where the lock file will be created
* @param resourceName - An alphanumeric name that describes the resource being locked. This will become
* the filename of the temporary file created to manage the lock.
* @param maxWaitMs - The maximum number of milliseconds to wait for the lock before reporting an error
*/
static async acquireAsync(resourceFolder, resourceName, maxWaitMs) {
const interval = 100;
const startTime = Date.now();
const timeoutTime = maxWaitMs ? startTime + maxWaitMs : undefined;
await FileSystem_1.FileSystem.ensureFolderAsync(resourceFolder);
const lockFilePath = LockFile.getLockFilePath(resourceFolder, resourceName);
// eslint-disable-next-line no-unmodified-loop-condition
while (!timeoutTime || Date.now() <= timeoutTime) {
const lock = LockFile._tryAcquireInner(resourceFolder, resourceName, lockFilePath);
if (lock) {
return lock;
}
await Async_1.Async.sleepAsync(interval);
}
throw new Error(`Exceeded maximum wait time to acquire lock for resource "${resourceName}"`);
}
static _tryAcquireInner(resourceFolder, resourceName, lockFilePath) {
if (!IN_PROC_LOCKS.has(lockFilePath)) {
switch (process.platform) {
case 'win32': {
return LockFile._tryAcquireWindows(lockFilePath);
}
case 'linux':
case 'darwin': {
return LockFile._tryAcquireMacOrLinux(resourceFolder, resourceName, lockFilePath);
}
default: {
throw new Error(`File locking not implemented for platform: "${process.platform}"`);
}
}
}
}
/**
* Attempts to acquire the lock on a Linux or OSX machine
*/
static _tryAcquireMacOrLinux(resourceFolder, resourceName, pidLockFilePath) {
let dirtyWhenAcquired = false;
// get the current process' pid
const pid = process.pid;
const startTime = LockFile._getStartTime(pid);
if (!startTime) {
throw new Error(`Unable to calculate start time for current process.`);
}
let lockFileHandle;
let lockFile;
try {
// open in write mode since if this file exists, it cannot be from the current process
// TODO: This will malfunction if the same process tries to acquire two locks on the same file.
// We should ideally maintain a dictionary of normalized acquired filenames
lockFileHandle = FileWriter_1.FileWriter.open(pidLockFilePath);
lockFileHandle.write(startTime);
const currentBirthTimeMs = lockFileHandle.getStatistics().birthtime.getTime();
let smallestBirthTimeMs = currentBirthTimeMs;
let smallestBirthTimePid = pid.toString();
// now, scan the directory for all lockfiles
const files = FileSystem_1.FileSystem.readFolderItemNames(resourceFolder);
// look for anything ending with # then numbers and ".lock"
const lockFileRegExp = /^(.+)#([0-9]+)\.lock$/;
let match;
let otherPid;
for (const fileInFolder of files) {
if ((match = fileInFolder.match(lockFileRegExp)) &&
match[1] === resourceName &&
(otherPid = match[2]) !== pid.toString()) {
// we found at least one lockfile hanging around that isn't ours
const fileInFolderPath = `${resourceFolder}/${fileInFolder}`;
dirtyWhenAcquired = true;
// console.log(`FOUND OTHER LOCKFILE: ${otherPid}`);
const otherPidCurrentStartTime = LockFile._getStartTime(parseInt(otherPid, 10));
let otherPidOldStartTime;
let otherBirthtimeMs;
try {
otherPidOldStartTime = FileSystem_1.FileSystem.readFile(fileInFolderPath);
// check the timestamp of the file
otherBirthtimeMs = FileSystem_1.FileSystem.getStatistics(fileInFolderPath).birthtime.getTime();
}
catch (error) {
if (FileSystem_1.FileSystem.isNotExistError(error)) {
// the file is already deleted by other process, skip it
continue;
}
}
// if the otherPidOldStartTime is invalid, then we should look at the timestamp,
// if this file was created after us, ignore it
// if it was created within 1 second before us, then it could be good, so we
// will conservatively fail
// otherwise it is an old lock file and will be deleted
if (otherPidOldStartTime === '' && otherBirthtimeMs !== undefined) {
if (otherBirthtimeMs > currentBirthTimeMs) {
// ignore this file, he will be unable to get the lock since this process
// will hold it
// console.log(`Ignoring lock for pid ${otherPid} because its lockfile is newer than ours.`);
continue;
}
else if (otherBirthtimeMs - currentBirthTimeMs < 0 && // it was created before us AND
otherBirthtimeMs - currentBirthTimeMs > -1000) {
// it was created less than a second before
// conservatively be unable to keep the lock
return undefined;
}
}
// console.log(`Other pid ${otherPid} lockfile has start time: "${otherPidOldStartTime}"`);
// console.log(`Other pid ${otherPid} actually has start time: "${otherPidCurrentStartTime}"`);
// this means the process is no longer executing, delete the file
if (!otherPidCurrentStartTime || otherPidOldStartTime !== otherPidCurrentStartTime) {
// console.log(`Other pid ${otherPid} is no longer executing!`);
FileSystem_1.FileSystem.deleteFile(fileInFolderPath);
continue;
}
// console.log(`Pid ${otherPid} lockfile has birth time: ${otherBirthtimeMs}`);
// console.log(`Pid ${pid} lockfile has birth time: ${currentBirthTimeMs}`);
// this is a lockfile pointing at something valid
if (otherBirthtimeMs !== undefined) {
// the other lock file was created before the current earliest lock file
// or the other lock file was created at the same exact time, but has earlier pid
// note that it is acceptable to do a direct comparison of the PIDs in this case
// since we are establishing a consistent order to apply to the lock files in all
// execution instances.
// it doesn't matter that the PIDs roll over, we've already
// established that these processes all started at the same time, so we just
// need to get all instances of the lock test to agree which one won.
if (otherBirthtimeMs < smallestBirthTimeMs ||
(otherBirthtimeMs === smallestBirthTimeMs && otherPid < smallestBirthTimePid)) {
smallestBirthTimeMs = otherBirthtimeMs;
smallestBirthTimePid = otherPid;
}
}
}
}
if (smallestBirthTimePid !== pid.toString()) {
// we do not have the lock
return undefined;
}
// we have the lock!
lockFile = new LockFile(lockFileHandle, pidLockFilePath, dirtyWhenAcquired);
lockFileHandle = undefined; // we have handed the descriptor off to the instance
}
finally {
if (lockFileHandle) {
// ensure our lock is closed
lockFileHandle.close();
FileSystem_1.FileSystem.deleteFile(pidLockFilePath);
}
}
return lockFile;
}
/**
* Attempts to acquire the lock using Windows
* This algorithm is much simpler since we can rely on the operating system
*/
static _tryAcquireWindows(lockFilePath) {
let dirtyWhenAcquired = false;
let fileHandle;
let lockFile;
try {
if (FileSystem_1.FileSystem.exists(lockFilePath)) {
dirtyWhenAcquired = true;
// If the lockfile is held by an process with an exclusive lock, then removing it will
// silently fail. OpenSync() below will then fail and we will be unable to create a lock.
// Otherwise, the lockfile is sitting on disk, but nothing is holding it, implying that
// the last process to hold it died.
FileSystem_1.FileSystem.deleteFile(lockFilePath);
}
try {
// Attempt to open an exclusive lockfile
fileHandle = FileWriter_1.FileWriter.open(lockFilePath, { exclusive: true });
}
catch (error) {
// we tried to delete the lock, but something else is holding it,
// (probably an active process), therefore we are unable to create a lock
return undefined;
}
// Ensure we can hand off the file descriptor to the lockfile
lockFile = new LockFile(fileHandle, lockFilePath, dirtyWhenAcquired);
fileHandle = undefined;
}
finally {
if (fileHandle) {
fileHandle.close();
}
}
return lockFile;
}
/**
* Unlocks a file and optionally removes it from disk.
* This can only be called once.
*
* @param deleteFile - Whether to delete the lockfile from disk. Defaults to true.
*/
release(deleteFile = true) {
if (this.isReleased) {
throw new Error(`The lock for file "${path.basename(this._filePath)}" has already been released.`);
}
IN_PROC_LOCKS.delete(this._filePath);
this._fileWriter.close();
if (deleteFile) {
FileSystem_1.FileSystem.deleteFile(this._filePath);
}
this._fileWriter = undefined;
}
/**
* Returns the initial state of the lock.
* This can be used to detect if the previous process was terminated before releasing the resource.
*/
get dirtyWhenAcquired() {
return this._dirtyWhenAcquired;
}
/**
* Returns the absolute path to the lockfile
*/
get filePath() {
return this._filePath;
}
/**
* Returns true if this lock is currently being held.
*/
get isReleased() {
return this._fileWriter === undefined;
}
}
exports.LockFile = LockFile;
LockFile._getStartTime = getProcessStartTime;
//# sourceMappingURL=LockFile.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"LockFile.js","sourceRoot":"","sources":["../src/LockFile.ts"],"names":[],"mappings":"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 truncated
@@ -0,0 +1,26 @@
/**
* Helper functions for working with the `Map<K, V>` data type.
*
* @public
*/
export declare class MapExtensions {
/**
* Adds all the (key, value) pairs from the source map into the target map.
* @remarks
* This function modifies targetMap. Any existing keys will be overwritten.
* @param targetMap - The map that entries will be added to
* @param sourceMap - The map containing the entries to be added
*/
static mergeFromMap<K, V>(targetMap: Map<K, V>, sourceMap: ReadonlyMap<K, V>): void;
/**
* Converts a string-keyed map to an object.
* @remarks
* This function has the same effect as Object.fromEntries(map.entries())
* in supported versions of Node (\>= 12.0.0).
* @param map - The map that the object properties will be sourced from
*/
static toObject<TValue>(map: Map<string, TValue>): {
[key: string]: TValue;
};
}
//# sourceMappingURL=MapExtensions.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"MapExtensions.d.ts","sourceRoot":"","sources":["../src/MapExtensions.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,qBAAa,aAAa;IACxB;;;;;;OAMG;WACW,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI;IAM1F;;;;;;OAMG;WACW,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE;CAOpF"}
+40
View File
@@ -0,0 +1,40 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.MapExtensions = void 0;
/**
* Helper functions for working with the `Map<K, V>` data type.
*
* @public
*/
class MapExtensions {
/**
* Adds all the (key, value) pairs from the source map into the target map.
* @remarks
* This function modifies targetMap. Any existing keys will be overwritten.
* @param targetMap - The map that entries will be added to
* @param sourceMap - The map containing the entries to be added
*/
static mergeFromMap(targetMap, sourceMap) {
for (const pair of sourceMap.entries()) {
targetMap.set(pair[0], pair[1]);
}
}
/**
* Converts a string-keyed map to an object.
* @remarks
* This function has the same effect as Object.fromEntries(map.entries())
* in supported versions of Node (\>= 12.0.0).
* @param map - The map that the object properties will be sourced from
*/
static toObject(map) {
const object = {};
for (const [key, value] of map.entries()) {
object[key] = value;
}
return object;
}
}
exports.MapExtensions = MapExtensions;
//# sourceMappingURL=MapExtensions.js.map
@@ -0,0 +1 @@
{"version":3,"file":"MapExtensions.js","sourceRoot":"","sources":["../src/MapExtensions.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D;;;;GAIG;AACH,MAAa,aAAa;IACxB;;;;;;OAMG;IACI,MAAM,CAAC,YAAY,CAAO,SAAoB,EAAE,SAA4B;QACjF,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC;YACvC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACI,MAAM,CAAC,QAAQ,CAAS,GAAwB;QACrD,MAAM,MAAM,GAA8B,EAAE,CAAC;QAC7C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;YACzC,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AA5BD,sCA4BC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n/**\n * Helper functions for working with the `Map<K, V>` data type.\n *\n * @public\n */\nexport class MapExtensions {\n /**\n * Adds all the (key, value) pairs from the source map into the target map.\n * @remarks\n * This function modifies targetMap. Any existing keys will be overwritten.\n * @param targetMap - The map that entries will be added to\n * @param sourceMap - The map containing the entries to be added\n */\n public static mergeFromMap<K, V>(targetMap: Map<K, V>, sourceMap: ReadonlyMap<K, V>): void {\n for (const pair of sourceMap.entries()) {\n targetMap.set(pair[0], pair[1]);\n }\n }\n\n /**\n * Converts a string-keyed map to an object.\n * @remarks\n * This function has the same effect as Object.fromEntries(map.entries())\n * in supported versions of Node (\\>= 12.0.0).\n * @param map - The map that the object properties will be sourced from\n */\n public static toObject<TValue>(map: Map<string, TValue>): { [key: string]: TValue } {\n const object: { [key: string]: TValue } = {};\n for (const [key, value] of map.entries()) {\n object[key] = value;\n }\n return object;\n }\n}\n"]}
+39
View File
@@ -0,0 +1,39 @@
/**
* Implements a standard heap data structure for items of type T and a custom comparator.
* The root will always be the minimum value as determined by the comparator.
*
* @public
*/
export declare class MinimumHeap<T> {
private readonly _items;
private readonly _comparator;
/**
* Constructs a new MinimumHeap instance.
* @param comparator - a comparator function that determines the order of the items in the heap.
* If the comparator returns a value less than zero, then `a` will be considered less than `b`.
* If the comparator returns zero, then `a` and `b` are considered equal.
* Otherwise, `a` will be considered greater than `b`.
*/
constructor(comparator: (a: T, b: T) => number);
/**
* Returns the number of items in the heap.
* @returns the number of items in the heap.
*/
get size(): number;
/**
* Retrieves the root item from the heap without removing it.
* @returns the root item, or `undefined` if the heap is empty
*/
peek(): T | undefined;
/**
* Retrieves and removes the root item from the heap. The next smallest item will become the new root.
* @returns the root item, or `undefined` if the heap is empty
*/
poll(): T | undefined;
/**
* Pushes an item into the heap.
* @param item - the item to push
*/
push(item: T): void;
}
//# sourceMappingURL=MinimumHeap.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"MinimumHeap.d.ts","sourceRoot":"","sources":["../src/MinimumHeap.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,qBAAa,WAAW,CAAC,CAAC;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyB;IAErD;;;;;;OAMG;gBACgB,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM;IAIrD;;;OAGG;IACH,IAAW,IAAI,IAAI,MAAM,CAExB;IAED;;;OAGG;IACI,IAAI,IAAI,CAAC,GAAG,SAAS;IAI5B;;;OAGG;IACI,IAAI,IAAI,CAAC,GAAG,SAAS;IA4C5B;;;OAGG;IACI,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI;CAe3B"}
+99
View File
@@ -0,0 +1,99 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.MinimumHeap = void 0;
/**
* Implements a standard heap data structure for items of type T and a custom comparator.
* The root will always be the minimum value as determined by the comparator.
*
* @public
*/
class MinimumHeap {
/**
* Constructs a new MinimumHeap instance.
* @param comparator - a comparator function that determines the order of the items in the heap.
* If the comparator returns a value less than zero, then `a` will be considered less than `b`.
* If the comparator returns zero, then `a` and `b` are considered equal.
* Otherwise, `a` will be considered greater than `b`.
*/
constructor(comparator) {
this._items = [];
this._comparator = comparator;
}
/**
* Returns the number of items in the heap.
* @returns the number of items in the heap.
*/
get size() {
return this._items.length;
}
/**
* Retrieves the root item from the heap without removing it.
* @returns the root item, or `undefined` if the heap is empty
*/
peek() {
return this._items[0];
}
/**
* Retrieves and removes the root item from the heap. The next smallest item will become the new root.
* @returns the root item, or `undefined` if the heap is empty
*/
poll() {
if (this.size > 0) {
const result = this._items[0];
const item = this._items.pop();
const size = this.size;
if (size === 0) {
// Short circuit in the trivial case
return result;
}
let index = 0;
let smallerChildIndex = 1;
while (smallerChildIndex < size) {
let smallerChild = this._items[smallerChildIndex];
const rightChildIndex = smallerChildIndex + 1;
if (rightChildIndex < size) {
const rightChild = this._items[rightChildIndex];
if (this._comparator(rightChild, smallerChild) < 0) {
smallerChildIndex = rightChildIndex;
smallerChild = rightChild;
}
}
if (this._comparator(smallerChild, item) < 0) {
this._items[index] = smallerChild;
index = smallerChildIndex;
smallerChildIndex = index * 2 + 1;
}
else {
break;
}
}
// Place the item in its final location satisfying the heap property
this._items[index] = item;
return result;
}
}
/**
* Pushes an item into the heap.
* @param item - the item to push
*/
push(item) {
let index = this.size;
while (index > 0) {
// Due to zero-based indexing the parent is not exactly a bit shift
const parentIndex = ((index + 1) >> 1) - 1;
const parent = this._items[parentIndex];
if (this._comparator(item, parent) < 0) {
this._items[index] = parent;
index = parentIndex;
}
else {
break;
}
}
this._items[index] = item;
}
}
exports.MinimumHeap = MinimumHeap;
//# sourceMappingURL=MinimumHeap.js.map
@@ -0,0 +1 @@
{"version":3,"file":"MinimumHeap.js","sourceRoot":"","sources":["../src/MinimumHeap.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D;;;;;GAKG;AACH,MAAa,WAAW;IAItB;;;;;;OAMG;IACH,YAAmB,UAAkC;QAVpC,WAAM,GAAQ,EAAE,CAAC;QAWhC,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;IAChC,CAAC;IAED;;;OAGG;IACH,IAAW,IAAI;QACb,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACI,IAAI;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACxB,CAAC;IAED;;;OAGG;IACI,IAAI;QACT,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YAClB,MAAM,MAAM,GAAM,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,IAAI,GAAM,IAAI,CAAC,MAAM,CAAC,GAAG,EAAG,CAAC;YAEnC,MAAM,IAAI,GAAW,IAAI,CAAC,IAAI,CAAC;YAC/B,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACf,oCAAoC;gBACpC,OAAO,MAAM,CAAC;YAChB,CAAC;YAED,IAAI,KAAK,GAAW,CAAC,CAAC;YAEtB,IAAI,iBAAiB,GAAW,CAAC,CAAC;YAElC,OAAO,iBAAiB,GAAG,IAAI,EAAE,CAAC;gBAChC,IAAI,YAAY,GAAM,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;gBAErD,MAAM,eAAe,GAAW,iBAAiB,GAAG,CAAC,CAAC;gBAEtD,IAAI,eAAe,GAAG,IAAI,EAAE,CAAC;oBAC3B,MAAM,UAAU,GAAM,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;oBACnD,IAAI,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;wBACnD,iBAAiB,GAAG,eAAe,CAAC;wBACpC,YAAY,GAAG,UAAU,CAAC;oBAC5B,CAAC;gBACH,CAAC;gBAED,IAAI,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC7C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,YAAY,CAAC;oBAClC,KAAK,GAAG,iBAAiB,CAAC;oBAC1B,iBAAiB,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC;gBACpC,CAAC;qBAAM,CAAC;oBACN,MAAM;gBACR,CAAC;YACH,CAAC;YAED,oEAAoE;YACpE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;YAE1B,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,IAAI,CAAC,IAAO;QACjB,IAAI,KAAK,GAAW,IAAI,CAAC,IAAI,CAAC;QAC9B,OAAO,KAAK,GAAG,CAAC,EAAE,CAAC;YACjB,mEAAmE;YACnE,MAAM,WAAW,GAAW,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YACnD,MAAM,MAAM,GAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YAC3C,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC;gBAC5B,KAAK,GAAG,WAAW,CAAC;YACtB,CAAC;iBAAM,CAAC;gBACN,MAAM;YACR,CAAC;QACH,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAC5B,CAAC;CACF;AAlGD,kCAkGC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n/**\n * Implements a standard heap data structure for items of type T and a custom comparator.\n * The root will always be the minimum value as determined by the comparator.\n *\n * @public\n */\nexport class MinimumHeap<T> {\n private readonly _items: T[] = [];\n private readonly _comparator: (a: T, b: T) => number;\n\n /**\n * Constructs a new MinimumHeap instance.\n * @param comparator - a comparator function that determines the order of the items in the heap.\n * If the comparator returns a value less than zero, then `a` will be considered less than `b`.\n * If the comparator returns zero, then `a` and `b` are considered equal.\n * Otherwise, `a` will be considered greater than `b`.\n */\n public constructor(comparator: (a: T, b: T) => number) {\n this._comparator = comparator;\n }\n\n /**\n * Returns the number of items in the heap.\n * @returns the number of items in the heap.\n */\n public get size(): number {\n return this._items.length;\n }\n\n /**\n * Retrieves the root item from the heap without removing it.\n * @returns the root item, or `undefined` if the heap is empty\n */\n public peek(): T | undefined {\n return this._items[0];\n }\n\n /**\n * Retrieves and removes the root item from the heap. The next smallest item will become the new root.\n * @returns the root item, or `undefined` if the heap is empty\n */\n public poll(): T | undefined {\n if (this.size > 0) {\n const result: T = this._items[0];\n const item: T = this._items.pop()!;\n\n const size: number = this.size;\n if (size === 0) {\n // Short circuit in the trivial case\n return result;\n }\n\n let index: number = 0;\n\n let smallerChildIndex: number = 1;\n\n while (smallerChildIndex < size) {\n let smallerChild: T = this._items[smallerChildIndex];\n\n const rightChildIndex: number = smallerChildIndex + 1;\n\n if (rightChildIndex < size) {\n const rightChild: T = this._items[rightChildIndex];\n if (this._comparator(rightChild, smallerChild) < 0) {\n smallerChildIndex = rightChildIndex;\n smallerChild = rightChild;\n }\n }\n\n if (this._comparator(smallerChild, item) < 0) {\n this._items[index] = smallerChild;\n index = smallerChildIndex;\n smallerChildIndex = index * 2 + 1;\n } else {\n break;\n }\n }\n\n // Place the item in its final location satisfying the heap property\n this._items[index] = item;\n\n return result;\n }\n }\n\n /**\n * Pushes an item into the heap.\n * @param item - the item to push\n */\n public push(item: T): void Line truncated
@@ -0,0 +1,142 @@
import type { IPackageJson, INodePackageJson } from './IPackageJson';
/**
* Constructor parameters for {@link PackageJsonLookup}
*
* @public
*/
export interface IPackageJsonLookupParameters {
/**
* Certain package.json fields such as "contributors" can be very large, and may
* significantly increase the memory footprint for the PackageJsonLookup cache.
* By default, PackageJsonLookup only loads a subset of standard commonly used
* fields names. Set loadExtraFields=true to always return all fields.
*/
loadExtraFields?: boolean;
}
/**
* This class provides methods for finding the nearest "package.json" for a folder
* and retrieving the name of the package. The results are cached.
*
* @public
*/
export declare class PackageJsonLookup {
private static _instance;
/**
* A singleton instance of `PackageJsonLookup`, which is useful for short-lived processes
* that can reasonably assume that the file system will not be modified after the cache
* is populated.
*
* @remarks
* For long-running processes that need to clear the cache at appropriate times,
* it is recommended to create your own instance of `PackageJsonLookup` instead
* of relying on this instance.
*/
static get instance(): PackageJsonLookup;
private _loadExtraFields;
private _packageFolderCache;
private _packageJsonCache;
constructor(parameters?: IPackageJsonLookupParameters);
/**
* A helper for loading the caller's own package.json file.
*
* @remarks
*
* This function provides a concise and efficient way for an NPM package to report metadata about itself.
* For example, a tool might want to report its version.
*
* The `loadOwnPackageJson()` probes upwards from the caller's folder, expecting to find a package.json file,
* which is assumed to be the caller's package. The result is cached, under the assumption that a tool's
* own package.json (and intermediary folders) will never change during the lifetime of the process.
*
* @example
* ```ts
* // Report the version of our NPM package
* const myPackageVersion: string = PackageJsonLookup.loadOwnPackageJson(__dirname).version;
* console.log(`Cool Tool - Version ${myPackageVersion}`);
* ```
*
* @param dirnameOfCaller - The NodeJS `__dirname` macro for the caller.
* @returns This function always returns a valid `IPackageJson` object. If any problems are encountered during
* loading, an exception will be thrown instead.
*/
static loadOwnPackageJson(dirnameOfCaller: string): IPackageJson;
/**
* Clears the internal file cache.
* @remarks
* Call this method if changes have been made to the package.json files on disk.
*/
clearCache(): void;
/**
* Returns the absolute path of a folder containing a package.json file, by looking
* upwards from the specified fileOrFolderPath. If no package.json can be found,
* undefined is returned.
*
* @remarks
* The fileOrFolderPath is not required to actually exist on disk.
* The fileOrFolderPath itself can be the return value, if it is a folder containing
* a package.json file.
* Both positive and negative lookup results are cached.
*
* @param fileOrFolderPath - a relative or absolute path to a source file or folder
* that may be part of a package
* @returns an absolute path to a folder containing a package.json file
*/
tryGetPackageFolderFor(fileOrFolderPath: string): string | undefined;
/**
* If the specified file or folder is part of a package, this returns the absolute path
* to the associated package.json file.
*
* @remarks
* The package folder is determined using the same algorithm
* as {@link PackageJsonLookup.tryGetPackageFolderFor}.
*
* @param fileOrFolderPath - a relative or absolute path to a source file or folder
* that may be part of a package
* @returns an absolute path to * package.json file
*/
tryGetPackageJsonFilePathFor(fileOrFolderPath: string): string | undefined;
/**
* If the specified file or folder is part of a package, this loads and returns the
* associated package.json file.
*
* @remarks
* The package folder is determined using the same algorithm
* as {@link PackageJsonLookup.tryGetPackageFolderFor}.
*
* @param fileOrFolderPath - a relative or absolute path to a source file or folder
* that may be part of a package
* @returns an IPackageJson object, or undefined if the fileOrFolderPath does not
* belong to a package
*/
tryLoadPackageJsonFor(fileOrFolderPath: string): IPackageJson | undefined;
/**
* This function is similar to {@link PackageJsonLookup.tryLoadPackageJsonFor}, except that it does not report
* an error if the `version` field is missing from the package.json file.
*/
tryLoadNodePackageJsonFor(fileOrFolderPath: string): INodePackageJson | undefined;
/**
* Loads the specified package.json file, if it is not already present in the cache.
*
* @remarks
* Unless {@link IPackageJsonLookupParameters.loadExtraFields} was specified,
* the returned IPackageJson object will contain a subset of essential fields.
* The returned object should be considered to be immutable; the caller must never
* modify it.
*
* @param jsonFilename - a relative or absolute path to a package.json file
*/
loadPackageJson(jsonFilename: string): IPackageJson;
/**
* This function is similar to {@link PackageJsonLookup.loadPackageJson}, except that it does not report an error
* if the `version` field is missing from the package.json file.
*/
loadNodePackageJson(jsonFilename: string): INodePackageJson;
private _loadPackageJsonInner;
/**
* Try to load a package.json file as an INodePackageJson,
* returning undefined if the found file does not contain a `name` field.
*/
private _tryLoadNodePackageJsonInner;
private _tryGetPackageFolderFor;
}
//# sourceMappingURL=PackageJsonLookup.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"PackageJsonLookup.d.ts","sourceRoot":"","sources":["../src/PackageJsonLookup.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAIrE;;;;GAIG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;;;;OAKG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA8BD;;;;;GAKG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAC,SAAS,CAAgC;IAExD;;;;;;;;;OASG;IACH,WAAkB,QAAQ,IAAI,iBAAiB,CAM9C;IAED,OAAO,CAAC,gBAAgB,CAAkB;IAI1C,OAAO,CAAC,mBAAmB,CAAmC;IAI9D,OAAO,CAAC,iBAAiB,CAA6B;gBAEnC,UAAU,CAAC,EAAE,4BAA4B;IAS5D;;;;;;;;;;;;;;;;;;;;;;OAsBG;WACW,kBAAkB,CAAC,eAAe,EAAE,MAAM,GAAG,YAAY;IAuBvE;;;;OAIG;IACI,UAAU,IAAI,IAAI;IAKzB;;;;;;;;;;;;;;OAcG;IACI,sBAAsB,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAiB3E;;;;;;;;;;;OAWG;IACI,4BAA4B,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAQjF;;;;;;;;;;;;OAYG;IACI,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAQhF;;;OAGG;IACI,yBAAyB,CAAC,gBAAgB,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAQxF;;;;;;;;;;OAUG;IACI,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,YAAY;IAU1D;;;OAGG;IACI,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,gBAAgB;IAIlE,OAAO,CAAC,qBAAqB;IAkC7B;;;OAGG;IACH,OAAO,CAAC,4BAA4B;IAkEpC,OAAO,CAAC,uBAAuB;CAkChC"}
@@ -0,0 +1,338 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.PackageJsonLookup = void 0;
const path = __importStar(require("path"));
const JsonFile_1 = require("./JsonFile");
const Constants_1 = require("./Constants");
const FileSystem_1 = require("./FileSystem");
/**
* This class provides methods for finding the nearest "package.json" for a folder
* and retrieving the name of the package. The results are cached.
*
* @public
*/
class PackageJsonLookup {
/**
* A singleton instance of `PackageJsonLookup`, which is useful for short-lived processes
* that can reasonably assume that the file system will not be modified after the cache
* is populated.
*
* @remarks
* For long-running processes that need to clear the cache at appropriate times,
* it is recommended to create your own instance of `PackageJsonLookup` instead
* of relying on this instance.
*/
static get instance() {
if (!PackageJsonLookup._instance) {
PackageJsonLookup._instance = new PackageJsonLookup({ loadExtraFields: true });
}
return PackageJsonLookup._instance;
}
constructor(parameters) {
this._loadExtraFields = false;
if (parameters) {
if (parameters.loadExtraFields) {
this._loadExtraFields = parameters.loadExtraFields;
}
}
this.clearCache();
}
/**
* A helper for loading the caller's own package.json file.
*
* @remarks
*
* This function provides a concise and efficient way for an NPM package to report metadata about itself.
* For example, a tool might want to report its version.
*
* The `loadOwnPackageJson()` probes upwards from the caller's folder, expecting to find a package.json file,
* which is assumed to be the caller's package. The result is cached, under the assumption that a tool's
* own package.json (and intermediary folders) will never change during the lifetime of the process.
*
* @example
* ```ts
* // Report the version of our NPM package
* const myPackageVersion: string = PackageJsonLookup.loadOwnPackageJson(__dirname).version;
* console.log(`Cool Tool - Version ${myPackageVersion}`);
* ```
*
* @param dirnameOfCaller - The NodeJS `__dirname` macro for the caller.
* @returns This function always returns a valid `IPackageJson` object. If any problems are encountered during
* loading, an exception will be thrown instead.
*/
static loadOwnPackageJson(dirnameOfCaller) {
const packageJson = PackageJsonLookup.instance.tryLoadPackageJsonFor(dirnameOfCaller);
if (packageJson === undefined) {
throw new Error(`PackageJsonLookup.loadOwnPackageJson() failed to find the caller's package.json.` +
` The __dirname was: ${dirnameOfCaller}`);
}
if (packageJson.version !== undefined) {
return packageJson;
}
const errorPath = PackageJsonLookup.instance.tryGetPackageJsonFilePathFor(dirnameOfCaller) || 'package.json';
throw new Error(`PackageJsonLookup.loadOwnPackageJson() failed because the "version" field is missing in` +
` ${errorPath}`);
}
/**
* Clears the internal file cache.
* @remarks
* Call this method if changes have been made to the package.json files on disk.
*/
clearCache() {
this._packageFolderCache = new Map();
this._packageJsonCache = new Map();
}
/**
* Returns the absolute path of a folder containing a package.json file, by looking
* upwards from the specified fileOrFolderPath. If no package.json can be found,
* undefined is returned.
*
* @remarks
* The fileOrFolderPath is not required to actually exist on disk.
* The fileOrFolderPath itself can be the return value, if it is a folder containing
* a package.json file.
* Both positive and negative lookup results are cached.
*
* @param fileOrFolderPath - a relative or absolute path to a source file or folder
* that may be part of a package
* @returns an absolute path to a folder containing a package.json file
*/
tryGetPackageFolderFor(fileOrFolderPath) {
// Convert it to an absolute path
const resolvedFileOrFolderPath = path.resolve(fileOrFolderPath);
// Optimistically hope that the starting string is already in the cache,
// in which case we can avoid disk access entirely.
//
// (Two lookups are required, because get() cannot distinguish the undefined value
// versus a missing key.)
if (this._packageFolderCache.has(resolvedFileOrFolderPath)) {
return this._packageFolderCache.get(resolvedFileOrFolderPath);
}
// Now call the recursive part of the algorithm
return this._tryGetPackageFolderFor(resolvedFileOrFolderPath);
}
/**
* If the specified file or folder is part of a package, this returns the absolute path
* to the associated package.json file.
*
* @remarks
* The package folder is determined using the same algorithm
* as {@link PackageJsonLookup.tryGetPackageFolderFor}.
*
* @param fileOrFolderPath - a relative or absolute path to a source file or folder
* that may be part of a package
* @returns an absolute path to * package.json file
*/
tryGetPackageJsonFilePathFor(fileOrFolderPath) {
const packageJsonFolder = this.tryGetPackageFolderFor(fileOrFolderPath);
if (!packageJsonFolder) {
return undefined;
}
return path.join(packageJsonFolder, Constants_1.FileConstants.PackageJson);
}
/**
* If the specified file or folder is part of a package, this loads and returns the
* associated package.json file.
*
* @remarks
* The package folder is determined using the same algorithm
* as {@link PackageJsonLookup.tryGetPackageFolderFor}.
*
* @param fileOrFolderPath - a relative or absolute path to a source file or folder
* that may be part of a package
* @returns an IPackageJson object, or undefined if the fileOrFolderPath does not
* belong to a package
*/
tryLoadPackageJsonFor(fileOrFolderPath) {
const packageJsonFilePath = this.tryGetPackageJsonFilePathFor(fileOrFolderPath);
if (!packageJsonFilePath) {
return undefined;
}
return this.loadPackageJson(packageJsonFilePath);
}
/**
* This function is similar to {@link PackageJsonLookup.tryLoadPackageJsonFor}, except that it does not report
* an error if the `version` field is missing from the package.json file.
*/
tryLoadNodePackageJsonFor(fileOrFolderPath) {
const packageJsonFilePath = this.tryGetPackageJsonFilePathFor(fileOrFolderPath);
if (!packageJsonFilePath) {
return undefined;
}
return this.loadNodePackageJson(packageJsonFilePath);
}
/**
* Loads the specified package.json file, if it is not already present in the cache.
*
* @remarks
* Unless {@link IPackageJsonLookupParameters.loadExtraFields} was specified,
* the returned IPackageJson object will contain a subset of essential fields.
* The returned object should be considered to be immutable; the caller must never
* modify it.
*
* @param jsonFilename - a relative or absolute path to a package.json file
*/
loadPackageJson(jsonFilename) {
const packageJson = this.loadNodePackageJson(jsonFilename);
if (!packageJson.version) {
throw new Error(`Error reading "${jsonFilename}":\n The required field "version" was not found`);
}
return packageJson;
}
/**
* This function is similar to {@link PackageJsonLookup.loadPackageJson}, except that it does not report an error
* if the `version` field is missing from the package.json file.
*/
loadNodePackageJson(jsonFilename) {
return this._loadPackageJsonInner(jsonFilename);
}
_loadPackageJsonInner(jsonFilename, errorsToIgnore) {
const loadResult = this._tryLoadNodePackageJsonInner(jsonFilename);
if (loadResult.error && (errorsToIgnore === null || errorsToIgnore === void 0 ? void 0 : errorsToIgnore.has(loadResult.error))) {
return undefined;
}
switch (loadResult.error) {
case 'FILE_NOT_FOUND': {
throw new Error(`Input file not found: ${jsonFilename}`);
}
case 'MISSING_NAME_FIELD': {
throw new Error(`Error reading "${jsonFilename}":\n The required field "name" was not found`);
}
case 'OTHER_ERROR': {
throw loadResult.errorObject;
}
default: {
return loadResult.packageJson;
}
}
}
/**
* Try to load a package.json file as an INodePackageJson,
* returning undefined if the found file does not contain a `name` field.
*/
_tryLoadNodePackageJsonInner(jsonFilename) {
// Since this will be a cache key, follow any symlinks and get an absolute path
// to minimize duplication. (Note that duplication can still occur due to e.g. character case.)
let normalizedFilePath;
try {
normalizedFilePath = FileSystem_1.FileSystem.getRealPath(jsonFilename);
}
catch (e) {
if (FileSystem_1.FileSystem.isNotExistError(e)) {
return {
error: 'FILE_NOT_FOUND'
};
}
else {
return {
error: 'OTHER_ERROR',
errorObject: e
};
}
}
let packageJson = this._packageJsonCache.get(normalizedFilePath);
if (!packageJson) {
const loadedPackageJson = JsonFile_1.JsonFile.load(normalizedFilePath);
// Make sure this is really a package.json file. CommonJS has fairly strict requirements,
// but NPM only requires "name" and "version"
if (!loadedPackageJson.name) {
return {
error: 'MISSING_NAME_FIELD'
};
}
if (this._loadExtraFields) {
packageJson = loadedPackageJson;
}
else {
packageJson = {};
// Unless "loadExtraFields" was requested, copy over the essential fields only
packageJson.bin = loadedPackageJson.bin;
packageJson.dependencies = loadedPackageJson.dependencies;
packageJson.description = loadedPackageJson.description;
packageJson.devDependencies = loadedPackageJson.devDependencies;
packageJson.exports = loadedPackageJson.exports;
packageJson.homepage = loadedPackageJson.homepage;
packageJson.license = loadedPackageJson.license;
packageJson.main = loadedPackageJson.main;
packageJson.name = loadedPackageJson.name;
packageJson.optionalDependencies = loadedPackageJson.optionalDependencies;
packageJson.peerDependencies = loadedPackageJson.peerDependencies;
packageJson.private = loadedPackageJson.private;
packageJson.scripts = loadedPackageJson.scripts;
packageJson.tsdocMetadata = loadedPackageJson.tsdocMetadata;
packageJson.typings = loadedPackageJson.typings || loadedPackageJson.types;
packageJson.version = loadedPackageJson.version;
}
Object.freeze(packageJson);
this._packageJsonCache.set(normalizedFilePath, packageJson);
}
return {
packageJson
};
}
// Recursive part of the algorithm from tryGetPackageFolderFor()
_tryGetPackageFolderFor(resolvedFileOrFolderPath) {
// Two lookups are required, because get() cannot distinguish the undefined value
// versus a missing key.
if (this._packageFolderCache.has(resolvedFileOrFolderPath)) {
return this._packageFolderCache.get(resolvedFileOrFolderPath);
}
// Is resolvedFileOrFolderPath itself a folder with a valid package.json file? If so, return it.
const packageJsonFilePath = `${resolvedFileOrFolderPath}/${Constants_1.FileConstants.PackageJson}`;
const packageJson = this._loadPackageJsonInner(packageJsonFilePath, new Set(['FILE_NOT_FOUND', 'MISSING_NAME_FIELD']));
if (packageJson) {
this._packageFolderCache.set(resolvedFileOrFolderPath, resolvedFileOrFolderPath);
return resolvedFileOrFolderPath;
}
// Otherwise go up one level
const parentFolder = path.dirname(resolvedFileOrFolderPath);
if (!parentFolder || parentFolder === resolvedFileOrFolderPath) {
// We reached the root directory without finding a package.json file,
// so cache the negative result
this._packageFolderCache.set(resolvedFileOrFolderPath, undefined);
return undefined; // no match
}
// Recurse upwards, caching every step along the way
const parentResult = this._tryGetPackageFolderFor(parentFolder);
// Cache the parent's answer as well
this._packageFolderCache.set(resolvedFileOrFolderPath, parentResult);
return parentResult;
}
}
exports.PackageJsonLookup = PackageJsonLookup;
//# sourceMappingURL=PackageJsonLookup.js.map
@@ -0,0 +1 @@
{"version":3,"file":"PackageJsonLookup.js","sourceRoot":"","sources":["../src/PackageJsonLookup.ts"],"names":[],"mappings":"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 truncated
+142
View File
@@ -0,0 +1,142 @@
/**
* A package name that has been separated into its scope and unscoped name.
*
* @public
*/
export interface IParsedPackageName {
/**
* The parsed NPM scope, or an empty string if there was no scope. The scope value will
* always include the at-sign.
* @remarks
* For example, if the parsed input was "\@scope/example", then scope would be "\@scope".
*/
scope: string;
/**
* The parsed NPM package name without the scope.
* @remarks
* For example, if the parsed input was "\@scope/example", then the name would be "example".
*/
unscopedName: string;
}
/**
* Result object returned by {@link PackageName.tryParse}
*
* @public
*/
export interface IParsedPackageNameOrError extends IParsedPackageName {
/**
* If the input string could not be parsed, then this string will contain a nonempty
* error message. Otherwise it will be an empty string.
*/
error: string;
}
/**
* Options that configure the validation rules used by a {@link PackageNameParser} instance.
*
* @remarks
* The default validation is based on the npmjs.com registry's policy for published packages, and includes these
* restrictions:
*
* - The package name cannot be longer than 214 characters.
*
* - The package name must not be empty.
*
* - Other than the `@` and `/` delimiters used for scopes, the only allowed characters
* are letters, numbers, `-`, `_`, and `.`.
*
* - The name must not start with a `.` or `_`.
*
* @public
*/
export interface IPackageNameParserOptions {
/**
* If true, allows upper-case letters in package names.
* This improves compatibility with some legacy private registries that still allow that.
*/
allowUpperCase?: boolean;
}
/**
* A configurable parser for validating and manipulating NPM package names such as `my-package` or `@scope/my-package`.
*
* @remarks
* If you do not need to customize the parser configuration, it is recommended to use {@link PackageName}
* which exposes these operations as a simple static class.
*
* @public
*/
export declare class PackageNameParser {
private static readonly _invalidNameCharactersRegExp;
private readonly _options;
constructor(options?: IPackageNameParserOptions);
/**
* This attempts to parse a package name that may include a scope component.
* The packageName must not be an empty string.
* @remarks
* This function will not throw an exception.
*
* @returns an {@link IParsedPackageNameOrError} structure whose `error` property will be
* nonempty if the string could not be parsed.
*/
tryParse(packageName: string): IParsedPackageNameOrError;
/**
* Same as {@link PackageName.tryParse}, except this throws an exception if the input
* cannot be parsed.
* @remarks
* The packageName must not be an empty string.
*/
parse(packageName: string): IParsedPackageName;
/**
* {@inheritDoc IParsedPackageName.scope}
*/
getScope(packageName: string): string;
/**
* {@inheritDoc IParsedPackageName.unscopedName}
*/
getUnscopedName(packageName: string): string;
/**
* Returns true if the specified package name is valid, or false otherwise.
* @remarks
* This function will not throw an exception.
*/
isValidName(packageName: string): boolean;
/**
* Throws an exception if the specified name is not a valid package name.
* The packageName must not be an empty string.
*/
validate(packageName: string): void;
/**
* Combines an optional package scope with an unscoped root name.
* @param scope - Must be either an empty string, or a scope name such as "\@example"
* @param unscopedName - Must be a nonempty package name that does not contain a scope
* @returns A full package name such as "\@example/some-library".
*/
combineParts(scope: string, unscopedName: string): string;
}
/**
* Provides basic operations for validating and manipulating NPM package names such as `my-package`
* or `@scope/my-package`.
*
* @remarks
* This is the default implementation of {@link PackageNameParser}, exposed as a convenient static class.
* If you need to configure the parsing rules, use `PackageNameParser` instead.
*
* @public
*/
export declare class PackageName {
private static readonly _parser;
/** {@inheritDoc PackageNameParser.tryParse} */
static tryParse(packageName: string): IParsedPackageNameOrError;
/** {@inheritDoc PackageNameParser.parse} */
static parse(packageName: string): IParsedPackageName;
/** {@inheritDoc PackageNameParser.getScope} */
static getScope(packageName: string): string;
/** {@inheritDoc PackageNameParser.getUnscopedName} */
static getUnscopedName(packageName: string): string;
/** {@inheritDoc PackageNameParser.isValidName} */
static isValidName(packageName: string): boolean;
/** {@inheritDoc PackageNameParser.validate} */
static validate(packageName: string): void;
/** {@inheritDoc PackageNameParser.combineParts} */
static combineParts(scope: string, unscopedName: string): string;
}
//# sourceMappingURL=PackageName.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"PackageName.d.ts","sourceRoot":"","sources":["../src/PackageName.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,yBAA0B,SAAQ,kBAAkB;IACnE;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;GAQG;AACH,qBAAa,iBAAiB;IAG5B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,4BAA4B,CAA+B;IAEnF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4B;gBAElC,OAAO,GAAE,yBAA8B;IAI1D;;;;;;;;OAQG;IACI,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,yBAAyB;IAmF/D;;;;;OAKG;IACI,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,kBAAkB;IAQrD;;OAEG;IACI,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAI5C;;OAEG;IACI,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAInD;;;;OAIG;IACI,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAKhD;;;OAGG;IACI,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAI1C;;;;;OAKG;IACI,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;CA6BjE;AAED;;;;;;;;;GASG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAA8C;IAE7E,+CAA+C;WACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,yBAAyB;IAItE,4CAA4C;WAC9B,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,kBAAkB;IAI5D,+CAA+C;WACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAInD,sDAAsD;WACxC,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAI1D,kDAAkD;WACpC,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAIvD,+CAA+C;WACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAIjD,mDAAmD;WACrC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;CAGxE"}
+213
View File
@@ -0,0 +1,213 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.PackageName = exports.PackageNameParser = void 0;
/**
* A configurable parser for validating and manipulating NPM package names such as `my-package` or `@scope/my-package`.
*
* @remarks
* If you do not need to customize the parser configuration, it is recommended to use {@link PackageName}
* which exposes these operations as a simple static class.
*
* @public
*/
class PackageNameParser {
constructor(options = {}) {
this._options = Object.assign({}, options);
}
/**
* This attempts to parse a package name that may include a scope component.
* The packageName must not be an empty string.
* @remarks
* This function will not throw an exception.
*
* @returns an {@link IParsedPackageNameOrError} structure whose `error` property will be
* nonempty if the string could not be parsed.
*/
tryParse(packageName) {
const result = {
scope: '',
unscopedName: '',
error: ''
};
let input = packageName;
if (input === null || input === undefined) {
result.error = 'The package name must not be null or undefined';
return result;
}
// Rule from npmjs.com:
// "The name must be less than or equal to 214 characters. This includes the scope for scoped packages."
if (packageName.length > 214) {
// Don't attempt to parse a ridiculously long input
result.error = 'The package name cannot be longer than 214 characters';
return result;
}
if (input[0] === '@') {
const indexOfScopeSlash = input.indexOf('/');
if (indexOfScopeSlash <= 0) {
result.scope = input;
result.error = `Error parsing "${packageName}": The scope must be followed by a slash`;
return result;
}
// Extract the scope substring
result.scope = input.substr(0, indexOfScopeSlash);
input = input.substr(indexOfScopeSlash + 1);
}
result.unscopedName = input;
if (result.scope === '@') {
result.error = `Error parsing "${packageName}": The scope name cannot be empty`;
return result;
}
if (result.unscopedName === '') {
result.error = 'The package name must not be empty';
return result;
}
// Rule from npmjs.com:
// "The name can't start with a dot or an underscore."
if (result.unscopedName[0] === '.' || result.unscopedName[0] === '_') {
result.error = `The package name "${packageName}" starts with an invalid character`;
return result;
}
// Convert "@scope/unscoped-name" --> "scopeunscoped-name"
const nameWithoutScopeSymbols = (result.scope ? result.scope.slice(1, -1) : '') + result.unscopedName;
if (!this._options.allowUpperCase) {
// "New packages must not have uppercase letters in the name."
// This can't be enforced because "old" packages are still actively maintained.
// Example: https://www.npmjs.com/package/Base64
// However it's pretty reasonable to require the scope to be lower case
if (result.scope !== result.scope.toLowerCase()) {
result.error = `The package scope "${result.scope}" must not contain upper case characters`;
return result;
}
}
// "The name ends up being part of a URL, an argument on the command line, and a folder name.
// Therefore, the name can't contain any non-URL-safe characters"
const match = nameWithoutScopeSymbols.match(PackageNameParser._invalidNameCharactersRegExp);
if (match) {
result.error = `The package name "${packageName}" contains an invalid character: "${match[0]}"`;
return result;
}
return result;
}
/**
* Same as {@link PackageName.tryParse}, except this throws an exception if the input
* cannot be parsed.
* @remarks
* The packageName must not be an empty string.
*/
parse(packageName) {
const result = this.tryParse(packageName);
if (result.error) {
throw new Error(result.error);
}
return result;
}
/**
* {@inheritDoc IParsedPackageName.scope}
*/
getScope(packageName) {
return this.parse(packageName).scope;
}
/**
* {@inheritDoc IParsedPackageName.unscopedName}
*/
getUnscopedName(packageName) {
return this.parse(packageName).unscopedName;
}
/**
* Returns true if the specified package name is valid, or false otherwise.
* @remarks
* This function will not throw an exception.
*/
isValidName(packageName) {
const result = this.tryParse(packageName);
return !result.error;
}
/**
* Throws an exception if the specified name is not a valid package name.
* The packageName must not be an empty string.
*/
validate(packageName) {
this.parse(packageName);
}
/**
* Combines an optional package scope with an unscoped root name.
* @param scope - Must be either an empty string, or a scope name such as "\@example"
* @param unscopedName - Must be a nonempty package name that does not contain a scope
* @returns A full package name such as "\@example/some-library".
*/
combineParts(scope, unscopedName) {
if (scope !== '') {
if (scope[0] !== '@') {
throw new Error('The scope must start with an "@" character');
}
}
if (scope.indexOf('/') >= 0) {
throw new Error('The scope must not contain a "/" character');
}
if (unscopedName[0] === '@') {
throw new Error('The unscopedName cannot start with an "@" character');
}
if (unscopedName.indexOf('/') >= 0) {
throw new Error('The unscopedName must not contain a "/" character');
}
let result;
if (scope === '') {
result = unscopedName;
}
else {
result = scope + '/' + unscopedName;
}
// Make sure the result is a valid package name
this.validate(result);
return result;
}
}
exports.PackageNameParser = PackageNameParser;
// encodeURIComponent() escapes all characters except: A-Z a-z 0-9 - _ . ! ~ * ' ( )
// However, these are disallowed because they are shell characters: ! ~ * ' ( )
PackageNameParser._invalidNameCharactersRegExp = /[^A-Za-z0-9\-_\.]/;
/**
* Provides basic operations for validating and manipulating NPM package names such as `my-package`
* or `@scope/my-package`.
*
* @remarks
* This is the default implementation of {@link PackageNameParser}, exposed as a convenient static class.
* If you need to configure the parsing rules, use `PackageNameParser` instead.
*
* @public
*/
class PackageName {
/** {@inheritDoc PackageNameParser.tryParse} */
static tryParse(packageName) {
return PackageName._parser.tryParse(packageName);
}
/** {@inheritDoc PackageNameParser.parse} */
static parse(packageName) {
return this._parser.parse(packageName);
}
/** {@inheritDoc PackageNameParser.getScope} */
static getScope(packageName) {
return this._parser.getScope(packageName);
}
/** {@inheritDoc PackageNameParser.getUnscopedName} */
static getUnscopedName(packageName) {
return this._parser.getUnscopedName(packageName);
}
/** {@inheritDoc PackageNameParser.isValidName} */
static isValidName(packageName) {
return this._parser.isValidName(packageName);
}
/** {@inheritDoc PackageNameParser.validate} */
static validate(packageName) {
return this._parser.validate(packageName);
}
/** {@inheritDoc PackageNameParser.combineParts} */
static combineParts(scope, unscopedName) {
return this._parser.combineParts(scope, unscopedName);
}
}
exports.PackageName = PackageName;
PackageName._parser = new PackageNameParser();
//# sourceMappingURL=PackageName.js.map
@@ -0,0 +1 @@
{"version":3,"file":"PackageName.js","sourceRoot":"","sources":["../src/PackageName.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n/**\n * A package name that has been separated into its scope and unscoped name.\n *\n * @public\n */\nexport interface IPLine truncated
+155
View File
@@ -0,0 +1,155 @@
/**
* The format that the FileError message should conform to. The supported formats are:
* - Unix: `<path>:<line>:<column> - <message>`
* - VisualStudio: `<path>(<line>,<column>) - <message>`
*
* @public
*/
export type FileLocationStyle = 'Unix' | 'VisualStudio';
/**
* Options for {@link Path.formatFileLocation}.
* @public
*/
export interface IPathFormatFileLocationOptions {
/**
* The base path to use when converting `pathToFormat` to a relative path. If not specified,
* `pathToFormat` will be used as-is.
*/
baseFolder?: string;
/**
* The path that will be used to specify the file location.
*/
pathToFormat: string;
/**
* The message related to the file location.
*/
message: string;
/**
* The style of file location formatting to use.
*/
format: FileLocationStyle;
/**
* The optional line number. If not specified, the line number will not be included
* in the formatted string.
*/
line?: number;
/**
* The optional column number. If not specified, the column number will not be included
* in the formatted string.
*/
column?: number;
}
/**
* Options for {@link Path.formatConcisely}.
* @public
*/
export interface IPathFormatConciselyOptions {
/**
* The path to be converted.
*/
pathToConvert: string;
/**
* The base path to use when converting `pathToConvert` to a relative path.
*/
baseFolder: string;
/**
* If set to true, don't include the leading `./` if the path is under the base folder.
*/
trimLeadingDotSlash?: boolean;
}
/**
* Common operations for manipulating file and directory paths.
* @remarks
* This API is intended to eventually be a complete replacement for the NodeJS "path" API.
* @public
*/
export declare class Path {
private static _relativePathRegex;
private static _upwardPathSegmentRegex;
/**
* Returns true if "childPath" is located inside the "parentFolderPath" folder
* or one of its child folders. Note that "parentFolderPath" is not considered to be
* under itself. The "childPath" can refer to any type of file system object.
*
* @remarks
* The indicated file/folder objects are not required to actually exist on disk.
* For example, "parentFolderPath" is interpreted as a folder name even if it refers to a file.
* If the paths are relative, they will first be resolved using path.resolve().
*/
static isUnder(childPath: string, parentFolderPath: string): boolean;
/**
* Returns true if "childPath" is equal to "parentFolderPath", or if it is inside that folder
* or one of its children. The "childPath" can refer to any type of file system object.
*
* @remarks
* The indicated file/folder objects are not required to actually exist on disk.
* For example, "parentFolderPath" is interpreted as a folder name even if it refers to a file.
* If the paths are relative, they will first be resolved using path.resolve().
*/
static isUnderOrEqual(childPath: string, parentFolderPath: string): boolean;
/**
* Returns true if `path1` and `path2` refer to the same underlying path.
*
* @remarks
*
* The comparison is performed using `path.relative()`.
*/
static isEqual(path1: string, path2: string): boolean;
/**
* Formats a path to look nice for reporting purposes.
* @remarks
* If `pathToConvert` is under the `baseFolder`, then it will be converted to a relative with the `./` prefix
* unless the {@link IPathFormatConciselyOptions.trimLeadingDotSlash} option is set to `true`.
* Otherwise, it will be converted to an absolute path.
*
* Backslashes will be converted to slashes, unless the path starts with an OS-specific string like `C:\`.
*/
static formatConcisely(options: IPathFormatConciselyOptions): string;
/**
* Formats a file location to look nice for reporting purposes.
* @remarks
* If `pathToFormat` is under the `baseFolder`, then it will be converted to a relative with the `./` prefix.
* Otherwise, it will be converted to an absolute path.
*
* Backslashes will be converted to slashes, unless the path starts with an OS-specific string like `C:\`.
*/
static formatFileLocation(options: IPathFormatFileLocationOptions): string;
/**
* Replaces Windows-style backslashes with POSIX-style slashes.
*
* @remarks
* POSIX is a registered trademark of the Institute of Electrical and Electronic Engineers, Inc.
*/
static convertToSlashes(inputPath: string): string;
/**
* Replaces POSIX-style slashes with Windows-style backslashes
*
* @remarks
* POSIX is a registered trademark of the Institute of Electrical and Electronic Engineers, Inc.
*/
static convertToBackslashes(inputPath: string): string;
/**
* Replaces slashes or backslashes with the appropriate slash for the current operating system.
*/
static convertToPlatformDefault(inputPath: string): string;
/**
* Returns true if the specified path is a relative path and does not use `..` to walk upwards.
*
* @example
* ```ts
* // These evaluate to true
* isDownwardRelative('folder');
* isDownwardRelative('file');
* isDownwardRelative('folder/');
* isDownwardRelative('./folder/');
* isDownwardRelative('./folder/file');
*
* // These evaluate to false
* isDownwardRelative('../folder');
* isDownwardRelative('folder/../file');
* isDownwardRelative('/folder/file');
* ```
*/
static isDownwardRelative(inputPath: string): boolean;
}
//# sourceMappingURL=Path.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Path.d.ts","sourceRoot":"","sources":["../src/Path.ts"],"names":[],"mappings":"AAKA;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,cAAc,CAAC;AAExD;;;GAGG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,MAAM,EAAE,iBAAiB,CAAC;IAC1B;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;GAKG;AACH,qBAAa,IAAI;IAGf,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAwB;IAIzD,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAAsC;IAE5E;;;;;;;;;OASG;WACW,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO;IAQ3E;;;;;;;;OAQG;WACW,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,OAAO;IAKlF;;;;;;OAMG;WACW,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAI5D;;;;;;;;OAQG;WACW,eAAe,CAAC,OAAO,EAAE,2BAA2B,GAAG,MAAM;IAqB3E;;;;;;;OAOG;WACW,kBAAkB,CAAC,OAAO,EAAE,8BAA8B,GAAG,MAAM;IA+CjF;;;;;OAKG;WACW,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIzD;;;;;OAKG;WACW,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAG7D;;OAEG;WACW,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIjE;;;;;;;;;;;;;;;;;OAiBG;WACW,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;CAU7D"}
+224
View File
@@ -0,0 +1,224 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Path = void 0;
const path = __importStar(require("path"));
/**
* Common operations for manipulating file and directory paths.
* @remarks
* This API is intended to eventually be a complete replacement for the NodeJS "path" API.
* @public
*/
class Path {
/**
* Returns true if "childPath" is located inside the "parentFolderPath" folder
* or one of its child folders. Note that "parentFolderPath" is not considered to be
* under itself. The "childPath" can refer to any type of file system object.
*
* @remarks
* The indicated file/folder objects are not required to actually exist on disk.
* For example, "parentFolderPath" is interpreted as a folder name even if it refers to a file.
* If the paths are relative, they will first be resolved using path.resolve().
*/
static isUnder(childPath, parentFolderPath) {
// If childPath is under parentPath, then relativePath will be something like
// "../.." or "..\\..", which consists entirely of periods and slashes.
// (Note that something like "....t" is actually a valid filename, but "...." is not.)
const relativePath = path.relative(childPath, parentFolderPath);
return Path._relativePathRegex.test(relativePath);
}
/**
* Returns true if "childPath" is equal to "parentFolderPath", or if it is inside that folder
* or one of its children. The "childPath" can refer to any type of file system object.
*
* @remarks
* The indicated file/folder objects are not required to actually exist on disk.
* For example, "parentFolderPath" is interpreted as a folder name even if it refers to a file.
* If the paths are relative, they will first be resolved using path.resolve().
*/
static isUnderOrEqual(childPath, parentFolderPath) {
const relativePath = path.relative(childPath, parentFolderPath);
return relativePath === '' || Path._relativePathRegex.test(relativePath);
}
/**
* Returns true if `path1` and `path2` refer to the same underlying path.
*
* @remarks
*
* The comparison is performed using `path.relative()`.
*/
static isEqual(path1, path2) {
return path.relative(path1, path2) === '';
}
/**
* Formats a path to look nice for reporting purposes.
* @remarks
* If `pathToConvert` is under the `baseFolder`, then it will be converted to a relative with the `./` prefix
* unless the {@link IPathFormatConciselyOptions.trimLeadingDotSlash} option is set to `true`.
* Otherwise, it will be converted to an absolute path.
*
* Backslashes will be converted to slashes, unless the path starts with an OS-specific string like `C:\`.
*/
static formatConcisely(options) {
// Same logic as Path.isUnderOrEqual()
const relativePath = path.relative(options.pathToConvert, options.baseFolder);
const isUnderOrEqual = relativePath === '' || Path._relativePathRegex.test(relativePath);
if (isUnderOrEqual) {
// Note that isUnderOrEqual()'s relativePath is the reverse direction
const convertedPath = Path.convertToSlashes(path.relative(options.baseFolder, options.pathToConvert));
if (options.trimLeadingDotSlash) {
return convertedPath;
}
else {
return `./${convertedPath}`;
}
}
const absolutePath = path.resolve(options.pathToConvert);
return absolutePath;
}
/**
* Formats a file location to look nice for reporting purposes.
* @remarks
* If `pathToFormat` is under the `baseFolder`, then it will be converted to a relative with the `./` prefix.
* Otherwise, it will be converted to an absolute path.
*
* Backslashes will be converted to slashes, unless the path starts with an OS-specific string like `C:\`.
*/
static formatFileLocation(options) {
const { message, format, pathToFormat, baseFolder, line, column } = options;
// Convert the path to be relative to the base folder, if specified. Otherwise, use
// the path as-is.
const filePath = baseFolder
? Path.formatConcisely({
pathToConvert: pathToFormat,
baseFolder,
trimLeadingDotSlash: true
})
: path.resolve(pathToFormat);
let formattedFileLocation;
switch (format) {
case 'Unix': {
if (line !== undefined && column !== undefined) {
formattedFileLocation = `:${line}:${column}`;
}
else if (line !== undefined) {
formattedFileLocation = `:${line}`;
}
else {
formattedFileLocation = '';
}
break;
}
case 'VisualStudio': {
if (line !== undefined && column !== undefined) {
formattedFileLocation = `(${line},${column})`;
}
else if (line !== undefined) {
formattedFileLocation = `(${line})`;
}
else {
formattedFileLocation = '';
}
break;
}
default: {
throw new Error(`Unknown format: ${format}`);
}
}
return `${filePath}${formattedFileLocation} - ${message}`;
}
/**
* Replaces Windows-style backslashes with POSIX-style slashes.
*
* @remarks
* POSIX is a registered trademark of the Institute of Electrical and Electronic Engineers, Inc.
*/
static convertToSlashes(inputPath) {
return inputPath.replace(/\\/g, '/');
}
/**
* Replaces POSIX-style slashes with Windows-style backslashes
*
* @remarks
* POSIX is a registered trademark of the Institute of Electrical and Electronic Engineers, Inc.
*/
static convertToBackslashes(inputPath) {
return inputPath.replace(/\//g, '\\');
}
/**
* Replaces slashes or backslashes with the appropriate slash for the current operating system.
*/
static convertToPlatformDefault(inputPath) {
return path.sep === '/' ? Path.convertToSlashes(inputPath) : Path.convertToBackslashes(inputPath);
}
/**
* Returns true if the specified path is a relative path and does not use `..` to walk upwards.
*
* @example
* ```ts
* // These evaluate to true
* isDownwardRelative('folder');
* isDownwardRelative('file');
* isDownwardRelative('folder/');
* isDownwardRelative('./folder/');
* isDownwardRelative('./folder/file');
*
* // These evaluate to false
* isDownwardRelative('../folder');
* isDownwardRelative('folder/../file');
* isDownwardRelative('/folder/file');
* ```
*/
static isDownwardRelative(inputPath) {
if (path.isAbsolute(inputPath)) {
return false;
}
// Does it contain ".."
if (Path._upwardPathSegmentRegex.test(inputPath)) {
return false;
}
return true;
}
}
exports.Path = Path;
// Matches a relative path consisting entirely of periods and slashes
// Example: ".", "..", "../..", etc
Path._relativePathRegex = /^[.\/\\]+$/;
// Matches a relative path segment that traverses upwards
// Example: "a/../b"
Path._upwardPathSegmentRegex = /([\/\\]|^)\.\.([\/\\]|$)/;
//# sourceMappingURL=Path.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"Path.js","sourceRoot":"","sources":["../src/Path.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport * as path from 'path';\n\n/**\n * The format that the FileError message should conform to. The supported formats are:\n * - Unix: `<path>:<line>:<column> - <message>`\n * - VisualStudio: `<path>(<line>,<column>) - <message>`\n *\n * @public\n */\nexport type FileLocationStyle = 'Unix' | 'VisualStudio';\n\n/**\n * Options for {@link Path.formatFileLocation}.\n * @public\n */\nexport interface IPathFormatFileLocationOptions {\n /**\n * The base path to use when converting `pathToFormat` to a relative path. If not specified,\n * `pathToFormat` will be used as-is.\n */\n baseFolder?: string;\n /**\n * The path that will be used to specify the file location.\n */\n pathToFormat: string;\n /**\n * The message related to the file location.\n */\n message: string;\n /**\n * The style of file location formatting to use.\n */\n format: FileLocationStyle;\n /**\n * The optional line number. If not specified, the line number will not be included\n * in the formatted string.\n */\n line?: number;\n /**\n * The optional column number. If not specified, the column number will not be included\n * in the formatted string.\n */\n column?: number;\n}\n\n/**\n * Options for {@link Path.formatConcisely}.\n * @public\n */\nexport interface IPathFormatConciselyOptions {\n /**\n * The path to be converted.\n */\n pathToConvert: string;\n\n /**\n * The base path to usLine truncated
@@ -0,0 +1,73 @@
/**
* An integer value used to specify file permissions for POSIX-like operating systems.
*
* @remarks
*
* This bitfield corresponds to the "mode_t" structure described in this document:
* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_stat.h.html
*
* It is used with NodeJS APIs such as fs.Stat.mode and fs.chmodSync(). These values
* represent a set of permissions and can be combined using bitwise arithmetic.
*
* POSIX is a registered trademark of the Institute of Electrical and Electronic Engineers, Inc.
*
* @public
*/
export declare enum PosixModeBits {
/**
* Indicates that the item's owner can read the item.
*/
UserRead = 256,
/**
* Indicates that the item's owner can modify the item.
*/
UserWrite = 128,
/**
* Indicates that the item's owner can execute the item (if it is a file)
* or search the item (if it is a directory).
*/
UserExecute = 64,
/**
* Indicates that users belonging to the item's group can read the item.
*/
GroupRead = 32,
/**
* Indicates that users belonging to the item's group can modify the item.
*/
GroupWrite = 16,
/**
* Indicates that users belonging to the item's group can execute the item (if it is a file)
* or search the item (if it is a directory).
*/
GroupExecute = 8,
/**
* Indicates that other users (besides the item's owner user or group) can read the item.
*/
OthersRead = 4,
/**
* Indicates that other users (besides the item's owner user or group) can modify the item.
*/
OthersWrite = 2,
/**
* Indicates that other users (besides the item's owner user or group) can execute the item (if it is a file)
* or search the item (if it is a directory).
*/
OthersExecute = 1,
/**
* A zero value where no permissions bits are set.
*/
None = 0,
/**
* An alias combining OthersRead, GroupRead, and UserRead permission bits.
*/
AllRead = 292,
/**
* An alias combining OthersWrite, GroupWrite, and UserWrite permission bits.
*/
AllWrite = 146,
/**
* An alias combining OthersExecute, GroupExecute, and UserExecute permission bits.
*/
AllExecute = 73
}
//# sourceMappingURL=PosixModeBits.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"PosixModeBits.d.ts","sourceRoot":"","sources":["../src/PosixModeBits.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;GAcG;AACH,oBAAY,aAAa;IAGvB;;OAEG;IACH,QAAQ,MAAS;IAEjB;;OAEG;IACH,SAAS,MAAS;IAElB;;;OAGG;IACH,WAAW,KAAS;IAEpB;;OAEG;IACH,SAAS,KAAS;IAElB;;OAEG;IACH,UAAU,KAAS;IAEnB;;;OAGG;IACH,YAAY,IAAS;IAErB;;OAEG;IACH,UAAU,IAAS;IAEnB;;OAEG;IACH,WAAW,IAAS;IAEpB;;;OAGG;IACH,aAAa,IAAS;IAItB;;OAEG;IACH,IAAI,IAAI;IAER;;OAEG;IACH,OAAO,MAAoC;IAE3C;;OAEG;IACH,QAAQ,MAAuC;IAE/C;;OAEG;IACH,UAAU,KAA6C;CACxD"}
+83
View File
@@ -0,0 +1,83 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.PosixModeBits = void 0;
// The PosixModeBits are intended to be used with bitwise operations.
/* eslint-disable no-bitwise */
/**
* An integer value used to specify file permissions for POSIX-like operating systems.
*
* @remarks
*
* This bitfield corresponds to the "mode_t" structure described in this document:
* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_stat.h.html
*
* It is used with NodeJS APIs such as fs.Stat.mode and fs.chmodSync(). These values
* represent a set of permissions and can be combined using bitwise arithmetic.
*
* POSIX is a registered trademark of the Institute of Electrical and Electronic Engineers, Inc.
*
* @public
*/
var PosixModeBits;
(function (PosixModeBits) {
// The bits
/**
* Indicates that the item's owner can read the item.
*/
PosixModeBits[PosixModeBits["UserRead"] = 256] = "UserRead";
/**
* Indicates that the item's owner can modify the item.
*/
PosixModeBits[PosixModeBits["UserWrite"] = 128] = "UserWrite";
/**
* Indicates that the item's owner can execute the item (if it is a file)
* or search the item (if it is a directory).
*/
PosixModeBits[PosixModeBits["UserExecute"] = 64] = "UserExecute";
/**
* Indicates that users belonging to the item's group can read the item.
*/
PosixModeBits[PosixModeBits["GroupRead"] = 32] = "GroupRead";
/**
* Indicates that users belonging to the item's group can modify the item.
*/
PosixModeBits[PosixModeBits["GroupWrite"] = 16] = "GroupWrite";
/**
* Indicates that users belonging to the item's group can execute the item (if it is a file)
* or search the item (if it is a directory).
*/
PosixModeBits[PosixModeBits["GroupExecute"] = 8] = "GroupExecute";
/**
* Indicates that other users (besides the item's owner user or group) can read the item.
*/
PosixModeBits[PosixModeBits["OthersRead"] = 4] = "OthersRead";
/**
* Indicates that other users (besides the item's owner user or group) can modify the item.
*/
PosixModeBits[PosixModeBits["OthersWrite"] = 2] = "OthersWrite";
/**
* Indicates that other users (besides the item's owner user or group) can execute the item (if it is a file)
* or search the item (if it is a directory).
*/
PosixModeBits[PosixModeBits["OthersExecute"] = 1] = "OthersExecute";
// Helpful aliases
/**
* A zero value where no permissions bits are set.
*/
PosixModeBits[PosixModeBits["None"] = 0] = "None";
/**
* An alias combining OthersRead, GroupRead, and UserRead permission bits.
*/
PosixModeBits[PosixModeBits["AllRead"] = 292] = "AllRead";
/**
* An alias combining OthersWrite, GroupWrite, and UserWrite permission bits.
*/
PosixModeBits[PosixModeBits["AllWrite"] = 146] = "AllWrite";
/**
* An alias combining OthersExecute, GroupExecute, and UserExecute permission bits.
*/
PosixModeBits[PosixModeBits["AllExecute"] = 73] = "AllExecute";
})(PosixModeBits || (exports.PosixModeBits = PosixModeBits = {}));
//# sourceMappingURL=PosixModeBits.js.map
@@ -0,0 +1 @@
{"version":3,"file":"PosixModeBits.js","sourceRoot":"","sources":["../src/PosixModeBits.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D,qEAAqE;AACrE,+BAA+B;AAE/B;;;;;;;;;;;;;;GAcG;AACH,IAAY,aAwEX;AAxED,WAAY,aAAa;IACvB,WAAW;IAEX;;OAEG;IACH,2DAAiB,CAAA;IAEjB;;OAEG;IACH,6DAAkB,CAAA;IAElB;;;OAGG;IACH,gEAAoB,CAAA;IAEpB;;OAEG;IACH,4DAAkB,CAAA;IAElB;;OAEG;IACH,8DAAmB,CAAA;IAEnB;;;OAGG;IACH,iEAAqB,CAAA;IAErB;;OAEG;IACH,6DAAmB,CAAA;IAEnB;;OAEG;IACH,+DAAoB,CAAA;IAEpB;;;OAGG;IACH,mEAAsB,CAAA;IAEtB,kBAAkB;IAElB;;OAEG;IACH,iDAAQ,CAAA;IAER;;OAEG;IACH,yDAA2C,CAAA;IAE3C;;OAEG;IACH,2DAA+C,CAAA;IAE/C;;OAEG;IACH,8DAAuD,CAAA;AACzD,CAAC,EAxEW,aAAa,6BAAb,aAAa,QAwExB","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n// The PosixModeBits are intended to be used with bitwise operations.\n/* eslint-disable no-bitwise */\n\n/**\n * An integer value used to specify file permissions for POSIX-like operating systems.\n *\n * @remarks\n *\n * This bitfield corresponds to the \"mode_t\" structure described in this document:\n * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_stat.h.html\n *\n * It is used with NodeJS APIs such as fs.Stat.mode and fs.chmodSync(). These values\n * represent a set of permissions and can be combined using bitwise arithmetic.\n *\n * POSIX is a registered trademark of the Institute of Electrical and Electronic Engineers, Inc.\n *\n * @public\n */\nexport enum PosixModeBits {\n // The bits\n\n /**\n * Indicates that the item's owner can read the item.\n */\n UserRead = 1 << 8,\n\n /**\n * Indicates that the item's owner can modify the item.\n */\n UserWrite = 1 << 7,\n\n /**\n * Indicates that the item's owner can execute the item (if it is a file)\n * or search the item (if it is a directory).\n */\n UserExecute = 1 << 6,\n\n /**\n * Indicates that users belonging to the item's group can read the item.\n */\n GroupRead = 1 << 5,\n\n /**\n * Indicates that users belonging to the item's group can modify the item.\n */\n GroupWrite = 1 << 4,\n\n /**\n * Indicates that users belonging to the item's group can execute the item (if it is a file)\n * or search the item (if it is a directory).\n */\n GroupExecute = 1 << 3,\n\n /**\n * Indicates that other users (besides the item's owner user or group) can read the item.\n */\n OthersRead = 1 << 2,\n\n /**\n * Indicates that other users (besides the item's owner user or group) can modify the item.\n */\n OthersWrite = 1 << 1,\n\n /**\n * Indicates that other users (besides the item's owner user or group) can execute the item (if it is a file)\n * or search the item (if it is a directory).\n */\n OthersExecute = 1 << 0,\n\n // Helpful aliases\n\n /**\n * A zero value where no permissions bits are set.\n */\n None = 0,\n\n /**\n * An alias combining OthersRead, GroupRead, and UserRead permission bits.\n */\n AllRead = OthersRead | GroupRead | UserRead,\n\n /**\n * An alias combining OthersWrite, GroupWrite, and UserWrite permission bits.\n */\n AllWrite = OthersWrite | GroupWrite | UserWrite,\n\n /**\n * An alias combining OthersExecute, GroupExecute, and UserExecute permission bits.\n */\n AllExecute = OthersExecute | GroupExecute | UserExecute\n}\n"]}
@@ -0,0 +1,41 @@
/**
* A "branded type" is a primitive type with a compile-type key that makes it incompatible with other
* aliases for the primitive type.
*
* @remarks
*
* Example usage:
*
* ```ts
* // PhoneNumber is a branded type based on the "string" primitive.
* type PhoneNumber = Brand<string, 'PhoneNumber'>;
*
* function createPhoneNumber(input: string): PhoneNumber {
* if (!/\d+(\-\d+)+/.test(input)) {
* throw new Error('Invalid phone number: ' + JSON.stringify(input));
* }
* return input as PhoneNumber;
* }
*
* const p1: PhoneNumber = createPhoneNumber('123-456-7890');
*
* // PhoneNumber is a string and can be used as one:
* const p2: string = p1;
*
* // But an arbitrary string cannot be implicitly type cast as PhoneNumber.
* // ERROR: Type 'string' is not assignable to type 'PhoneNumber'
* const p3: PhoneNumber = '123-456-7890';
* ```
*
* For more information about this pattern, see {@link
* https://github.com/Microsoft/TypeScript/blob/7b48a182c05ea4dea81bab73ecbbe9e013a79e99/src/compiler/types.ts#L693-L698
* | this comment} explaining the TypeScript compiler's introduction of this pattern, and
* {@link https://spin.atomicobject.com/2018/01/15/typescript-flexible-nominal-typing/ | this article}
* explaining the technique in depth.
*
* @public
*/
export type Brand<T, BrandTag extends string> = T & {
__brand: BrandTag;
};
//# sourceMappingURL=PrimitiveTypes.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"PrimitiveTypes.d.ts","sourceRoot":"","sources":["../src/PrimitiveTypes.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,MAAM,KAAK,CAAC,CAAC,EAAE,QAAQ,SAAS,MAAM,IAAI,CAAC,GAAG;IAAE,OAAO,EAAE,QAAQ,CAAA;CAAE,CAAC"}
@@ -0,0 +1,5 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=PrimitiveTypes.js.map
@@ -0,0 +1 @@
{"version":3,"file":"PrimitiveTypes.js","sourceRoot":"","sources":["../src/PrimitiveTypes.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\n/**\n * A \"branded type\" is a primitive type with a compile-type key that makes it incompatible with other\n * aliases for the primitive type.\n *\n * @remarks\n *\n * Example usage:\n *\n * ```ts\n * // PhoneNumber is a branded type based on the \"string\" primitive.\n * type PhoneNumber = Brand<string, 'PhoneNumber'>;\n *\n * function createPhoneNumber(input: string): PhoneNumber {\n * if (!/\\d+(\\-\\d+)+/.test(input)) {\n * throw new Error('Invalid phone number: ' + JSON.stringify(input));\n * }\n * return input as PhoneNumber;\n * }\n *\n * const p1: PhoneNumber = createPhoneNumber('123-456-7890');\n *\n * // PhoneNumber is a string and can be used as one:\n * const p2: string = p1;\n *\n * // But an arbitrary string cannot be implicitly type cast as PhoneNumber.\n * // ERROR: Type 'string' is not assignable to type 'PhoneNumber'\n * const p3: PhoneNumber = '123-456-7890';\n * ```\n *\n * For more information about this pattern, see {@link\n * https://github.com/Microsoft/TypeScript/blob/7b48a182c05ea4dea81bab73ecbbe9e013a79e99/src/compiler/types.ts#L693-L698\n * | this comment} explaining the TypeScript compiler's introduction of this pattern, and\n * {@link https://spin.atomicobject.com/2018/01/15/typescript-flexible-nominal-typing/ | this article}\n * explaining the technique in depth.\n *\n * @public\n */\nexport type Brand<T, BrandTag extends string> = T & { __brand: BrandTag };\n"]}
@@ -0,0 +1,83 @@
/**
* Constructor parameters for {@link ProtectableMap}
*
* @public
*/
export interface IProtectableMapParameters<K, V> {
/**
* An optional hook that will be invoked before Map.clear() is performed.
*/
onClear?: (source: ProtectableMap<K, V>) => void;
/**
* An optional hook that will be invoked before Map.delete() is performed.
*/
onDelete?: (source: ProtectableMap<K, V>, key: K) => void;
/**
* An optional hook that will be invoked before Map.set() is performed.
* @remarks
* If this hook is provided, the function MUST return the `value` parameter.
* This provides the opportunity to modify the value before it is added
* to the map.
*/
onSet?: (source: ProtectableMap<K, V>, key: K, value: V) => V;
}
/**
* The ProtectableMap provides an easy way for an API to expose a `Map<K, V>` property
* while intercepting and validating any write operations that are performed by
* consumers of the API.
*
* @remarks
* The ProtectableMap itself is intended to be a private object that only its owner
* can access directly. Any operations performed directly on the ProtectableMap will
* bypass the hooks and any validation they perform. The public property that is exposed
* to API consumers should return {@link ProtectableMap.protectedView} instead.
*
* For example, suppose you want to share your `Map<string, number>` data structure,
* but you want to enforce that the key must always be an upper case string:
* You could use the onSet() hook to validate the keys and throw an exception
* if the key is not uppercase.
*
* @public
*/
export declare class ProtectableMap<K, V> {
private readonly _protectedView;
constructor(parameters: IProtectableMapParameters<K, V>);
/**
* The owner of the protectable map should return this object via its public API.
*/
get protectedView(): Map<K, V>;
/**
* Removes all entries from the map.
* This operation does NOT invoke the ProtectableMap onClear() hook.
*/
clear(): void;
/**
* Removes the specified key from the map.
* This operation does NOT invoke the ProtectableMap onDelete() hook.
*/
delete(key: K): boolean;
/**
* Sets a value for the specified key.
* This operation does NOT invoke the ProtectableMap onSet() hook.
*/
set(key: K, value: V): this;
/**
* Performs an operation for each (key, value) entries in the map.
*/
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void;
/**
* Retrieves the value for the specified key.
* @returns undefined if the value is undefined OR if the key is missing;
* otherwise returns the value associated with the key.
*/
get(key: K): V | undefined;
/**
* Returns true if the specified key belongs to the map.
*/
has(key: K): boolean;
/**
* Returns the number of (key, value) entries in the map.
*/
get size(): number;
}
//# sourceMappingURL=ProtectableMap.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"ProtectableMap.d.ts","sourceRoot":"","sources":["../src/ProtectableMap.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,MAAM,WAAW,yBAAyB,CAAC,CAAC,EAAE,CAAC;IAC7C;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;IAEjD;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC;IAE1D;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;CAC/D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,cAAc,CAAC,CAAC,EAAE,CAAC;IAC9B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA2B;gBAEvC,UAAU,EAAE,yBAAyB,CAAC,CAAC,EAAE,CAAC,CAAC;IAI9D;;OAEG;IACH,IAAW,aAAa,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAEpC;IAKD;;;OAGG;IACI,KAAK,IAAI,IAAI;IAIpB;;;OAGG;IACI,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAI9B;;;OAGG;IACI,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAQlC;;OAEG;IAEI,OAAO,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,IAAI;IAI3F;;;;OAIG;IACI,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAIjC;;OAEG;IACI,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAI3B;;OAEG;IACH,IAAW,IAAI,IAAI,MAAM,CAExB;CACF"}
+90
View File
@@ -0,0 +1,90 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProtectableMap = void 0;
const ProtectableMapView_1 = require("./ProtectableMapView");
/**
* The ProtectableMap provides an easy way for an API to expose a `Map<K, V>` property
* while intercepting and validating any write operations that are performed by
* consumers of the API.
*
* @remarks
* The ProtectableMap itself is intended to be a private object that only its owner
* can access directly. Any operations performed directly on the ProtectableMap will
* bypass the hooks and any validation they perform. The public property that is exposed
* to API consumers should return {@link ProtectableMap.protectedView} instead.
*
* For example, suppose you want to share your `Map<string, number>` data structure,
* but you want to enforce that the key must always be an upper case string:
* You could use the onSet() hook to validate the keys and throw an exception
* if the key is not uppercase.
*
* @public
*/
class ProtectableMap {
constructor(parameters) {
this._protectedView = new ProtectableMapView_1.ProtectableMapView(this, parameters);
}
/**
* The owner of the protectable map should return this object via its public API.
*/
get protectedView() {
return this._protectedView;
}
// ---------------------------------------------------------------------------
// lib.es2015.collections contract - write operations
/**
* Removes all entries from the map.
* This operation does NOT invoke the ProtectableMap onClear() hook.
*/
clear() {
this._protectedView._clearUnprotected();
}
/**
* Removes the specified key from the map.
* This operation does NOT invoke the ProtectableMap onDelete() hook.
*/
delete(key) {
return this._protectedView._deleteUnprotected(key);
}
/**
* Sets a value for the specified key.
* This operation does NOT invoke the ProtectableMap onSet() hook.
*/
set(key, value) {
this._protectedView._setUnprotected(key, value);
return this;
}
// ---------------------------------------------------------------------------
// lib.es2015.collections contract - read operations
/**
* Performs an operation for each (key, value) entries in the map.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
forEach(callbackfn, thisArg) {
this._protectedView.forEach(callbackfn);
}
/**
* Retrieves the value for the specified key.
* @returns undefined if the value is undefined OR if the key is missing;
* otherwise returns the value associated with the key.
*/
get(key) {
return this._protectedView.get(key);
}
/**
* Returns true if the specified key belongs to the map.
*/
has(key) {
return this._protectedView.has(key);
}
/**
* Returns the number of (key, value) entries in the map.
*/
get size() {
return this._protectedView.size;
}
}
exports.ProtectableMap = ProtectableMap;
//# sourceMappingURL=ProtectableMap.js.map
@@ -0,0 +1 @@
{"version":3,"file":"ProtectableMap.js","sourceRoot":"","sources":["../src/ProtectableMap.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAE3D,6DAA0D;AA4B1D;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAa,cAAc;IAGzB,YAAmB,UAA2C;QAC5D,IAAI,CAAC,cAAc,GAAG,IAAI,uCAAkB,CAAO,IAAI,EAAE,UAAU,CAAC,CAAC;IACvE,CAAC;IAED;;OAEG;IACH,IAAW,aAAa;QACtB,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAED,8EAA8E;IAC9E,qDAAqD;IAErD;;;OAGG;IACI,KAAK;QACV,IAAI,CAAC,cAAc,CAAC,iBAAiB,EAAE,CAAC;IAC1C,CAAC;IAED;;;OAGG;IACI,MAAM,CAAC,GAAM;QAClB,OAAO,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACrD,CAAC;IAED;;;OAGG;IACI,GAAG,CAAC,GAAM,EAAE,KAAQ;QACzB,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,8EAA8E;IAC9E,oDAAoD;IAEpD;;OAEG;IACH,8DAA8D;IACvD,OAAO,CAAC,UAAsD,EAAE,OAAa;QAClF,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1C,CAAC;IAED;;;;OAIG;IACI,GAAG,CAAC,GAAM;QACf,OAAO,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IAED;;OAEG;IACI,GAAG,CAAC,GAAM;QACf,OAAO,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,IAAW,IAAI;QACb,OAAO,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC;IAClC,CAAC;CACF;AA3ED,wCA2EC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport { ProtectableMapView } from './ProtectableMapView';\n\n/**\n * Constructor parameters for {@link ProtectableMap}\n *\n * @public\n */\nexport interface IProtectableMapParameters<K, V> {\n /**\n * An optional hook that will be invoked before Map.clear() is performed.\n */\n onClear?: (source: ProtectableMap<K, V>) => void;\n\n /**\n * An optional hook that will be invoked before Map.delete() is performed.\n */\n onDelete?: (source: ProtectableMap<K, V>, key: K) => void;\n\n /**\n * An optional hook that will be invoked before Map.set() is performed.\n * @remarks\n * If this hook is provided, the function MUST return the `value` parameter.\n * This provides the opportunity to modify the value before it is added\n * to the map.\n */\n onSet?: (source: ProtectableMap<K, V>, key: K, value: V) => V;\n}\n\n/**\n * The ProtectableMap provides an easy way for an API to expose a `Map<K, V>` property\n * while intercepting and validating any write operations that are performed by\n * consumers of the API.\n *\n * @remarks\n * The ProtectableMap itself is intended to be a private object that only its owner\n * can access directly. Any operations performed directly on the ProtectableMap will\n * bypass the hooks and any validation they perform. The public property that is exposed\n * to API consumers should return {@link ProtectableMap.protectedView} instead.\n *\n * For example, suppose you want to share your `Map<string, number>` data structure,\n * but you want to enforce that the key must always be an upper case string:\n * You could use the onSet() hook to validate the keys and throw an exception\n * if the key is not uppercase.\n *\n * @public\n */\nexport class ProtectableMap<K, V> {\n private readonly _protectedView: ProtectableMapView<K, V>;\n\n public constructor(parameters: IProtectableMapParameters<K, V>) {\n this._protectedView = new ProtectableMapView<K, V>(this, parameters);\n }\n\n /**\n * The owner of the protectable map should return this object via its public API.\n */\n public get protectedView(): Map<K, V> {\n return this._protectedView;\n }\n\n // ---------------------------------------------------------------------------\n // lib.es2015.collections contract - write operations\n\n /**\n * Removes all entries from the map.\n * This operation does NOT invoke the ProtectableMap onClear() hook.\n */\n public clear(): void {\n this._protectedView._clearUnprotected();\n }\n\n /**\n * Removes the specified key from the map.\n * This operation does NOT invoke the ProtectableMap onDelete() hook.\n */\n public delete(key: K): boolean {\n return this._protectedView._deleteUnprotected(key);\n }\n\n /**\n * Sets a value for the specified key.\n * This operation does NOT invoke the ProtectableMap onSet() hook.\n */\n public set(key: K, value: V): this {\n this._protectedView._setUnprotected(key, value);\n return this;\n }\n\n // ---------------------------------------------------------------------------\n // lib.es2015.collections contract - read operations\n\n /**\n * Performs an operation for each (key, value) entries in the map.\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n public forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void {\n this._protectedView.forEach(callbackfn);\n }\n\n /**\n * Retrieves the value for the specified key.\n * @returns undefined if the value is undefined OR if the key is missing;\n * otherwise returns the value associated with the key.\n */\n public get(key: K): V | undefined {\n return this._protectedView.get(key);\nLine truncated
@@ -0,0 +1,20 @@
import type { ProtectableMap, IProtectableMapParameters } from './ProtectableMap';
/**
* The internal wrapper used by ProtectableMap. It extends the real `Map<K, V>` base class,
* but hooks the destructive operations (clear/delete/set) to give the owner a chance
* to block them.
*
* NOTE: This is not a public API.
*/
export declare class ProtectableMapView<K, V> extends Map<K, V> {
private readonly _owner;
private readonly _parameters;
constructor(owner: ProtectableMap<K, V>, parameters: IProtectableMapParameters<K, V>);
clear(): void;
delete(key: K): boolean;
set(key: K, value: V): this;
_clearUnprotected(): void;
_deleteUnprotected(key: K): boolean;
_setUnprotected(key: K, value: V): void;
}
//# sourceMappingURL=ProtectableMapView.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"ProtectableMapView.d.ts","sourceRoot":"","sources":["../src/ProtectableMapView.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAElF;;;;;;GAMG;AACH,qBAAa,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAE,SAAQ,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuB;IAC9C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkC;gBAE3C,KAAK,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,yBAAyB,CAAC,CAAC,EAAE,CAAC,CAAC;IAOpF,KAAK,IAAI,IAAI;IAQb,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAQvB,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAW3B,iBAAiB,IAAI,IAAI;IAKzB,kBAAkB,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAKnC,eAAe,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;CAG/C"}
@@ -0,0 +1,56 @@
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProtectableMapView = void 0;
/**
* The internal wrapper used by ProtectableMap. It extends the real `Map<K, V>` base class,
* but hooks the destructive operations (clear/delete/set) to give the owner a chance
* to block them.
*
* NOTE: This is not a public API.
*/
class ProtectableMapView extends Map {
constructor(owner, parameters) {
super();
this._owner = owner;
this._parameters = parameters;
}
clear() {
// override
if (this._parameters.onClear) {
this._parameters.onClear(this._owner);
}
super.clear();
}
delete(key) {
// override
if (this._parameters.onDelete) {
this._parameters.onDelete(this._owner, key);
}
return super.delete(key);
}
set(key, value) {
// override
let modifiedValue = value;
if (this._parameters.onSet) {
modifiedValue = this._parameters.onSet(this._owner, key, modifiedValue);
}
super.set(key, modifiedValue);
return this;
}
// INTERNAL USAGE ONLY
_clearUnprotected() {
super.clear();
}
// INTERNAL USAGE ONLY
_deleteUnprotected(key) {
return super.delete(key);
}
// INTERNAL USAGE ONLY
_setUnprotected(key, value) {
super.set(key, value);
}
}
exports.ProtectableMapView = ProtectableMapView;
//# sourceMappingURL=ProtectableMapView.js.map
@@ -0,0 +1 @@
{"version":3,"file":"ProtectableMapView.js","sourceRoot":"","sources":["../src/ProtectableMapView.ts"],"names":[],"mappings":";AAAA,4FAA4F;AAC5F,2DAA2D;;;AAI3D;;;;;;GAMG;AACH,MAAa,kBAAyB,SAAQ,GAAS;IAIrD,YAAmB,KAA2B,EAAE,UAA2C;QACzF,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;IAChC,CAAC;IAEM,KAAK;QACV,WAAW;QACX,IAAI,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxC,CAAC;QACD,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;IAEM,MAAM,CAAC,GAAM;QAClB,WAAW;QACX,IAAI,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC;YAC9B,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAEM,GAAG,CAAC,GAAM,EAAE,KAAQ;QACzB,WAAW;QACX,IAAI,aAAa,GAAM,KAAK,CAAC;QAC7B,IAAI,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;YAC3B,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC;QAC1E,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,sBAAsB;IACf,iBAAiB;QACtB,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;IAED,sBAAsB;IACf,kBAAkB,CAAC,GAAM;QAC9B,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED,sBAAsB;IACf,eAAe,CAAC,GAAM,EAAE,KAAQ;QACrC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACxB,CAAC;CACF;AAnDD,gDAmDC","sourcesContent":["// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.\n// See LICENSE in the project root for license information.\n\nimport type { ProtectableMap, IProtectableMapParameters } from './ProtectableMap';\n\n/**\n * The internal wrapper used by ProtectableMap. It extends the real `Map<K, V>` base class,\n * but hooks the destructive operations (clear/delete/set) to give the owner a chance\n * to block them.\n *\n * NOTE: This is not a public API.\n */\nexport class ProtectableMapView<K, V> extends Map<K, V> {\n private readonly _owner: ProtectableMap<K, V>;\n private readonly _parameters: IProtectableMapParameters<K, V>;\n\n public constructor(owner: ProtectableMap<K, V>, parameters: IProtectableMapParameters<K, V>) {\n super();\n\n this._owner = owner;\n this._parameters = parameters;\n }\n\n public clear(): void {\n // override\n if (this._parameters.onClear) {\n this._parameters.onClear(this._owner);\n }\n super.clear();\n }\n\n public delete(key: K): boolean {\n // override\n if (this._parameters.onDelete) {\n this._parameters.onDelete(this._owner, key);\n }\n return super.delete(key);\n }\n\n public set(key: K, value: V): this {\n // override\n let modifiedValue: V = value;\n if (this._parameters.onSet) {\n modifiedValue = this._parameters.onSet(this._owner, key, modifiedValue);\n }\n super.set(key, modifiedValue);\n return this;\n }\n\n // INTERNAL USAGE ONLY\n public _clearUnprotected(): void {\n super.clear();\n }\n\n // INTERNAL USAGE ONLY\n public _deleteUnprotected(key: K): boolean {\n return super.delete(key);\n }\n\n // INTERNAL USAGE ONLY\n public _setUnprotected(key: K, value: V): void {\n super.set(key, value);\n }\n}\n"]}
Loaded 100 of 65041 files, more files were not shown because too many files have changed in this diff. Show more