@microsoft/compose-language-service
Advanced tools
Comparing version 0.0.2-alpha to 0.0.3-alpha
@@ -0,1 +1,5 @@ | ||
## 0.0.3-alpha - 8 November 2021 | ||
### Fixed | ||
* A handful of minor bugs relating to position logic (especially affecting hover). | ||
## 0.0.2-alpha - 29 October 2021 | ||
@@ -2,0 +6,0 @@ ### Added |
@@ -33,3 +33,4 @@ /*!-------------------------------------------------------------------------------------------- | ||
interface TelemetryProperties { | ||
result?: 'Succeeded' | 'Failed' | 'Canceled'; | ||
isActivationEvent: 'true' | 'false'; | ||
result: 'Succeeded' | 'Failed' | 'Canceled'; | ||
error?: string; | ||
@@ -36,0 +37,0 @@ errorMessage?: string; |
@@ -12,2 +12,3 @@ "use strict"; | ||
properties: { | ||
isActivationEvent: 'false', | ||
result: 'Succeeded', | ||
@@ -14,0 +15,0 @@ }, |
@@ -192,2 +192,13 @@ "use strict"; | ||
/* eslint-disable @typescript-eslint/no-non-null-assertion */ | ||
if ((result = LineWithCommentRegex.exec(currentLine))) { | ||
const commentSepPosition = currentLine.indexOf(result.groups['commentSep']); | ||
// If the cursor is in a comment, then it's not "in" the document per se, but in the comment | ||
// So the path will only be "/<comment>", and the cursorIndentDepth is set to -1 to short-circuit the loop above | ||
if (params.position.character > commentSepPosition) { | ||
return { | ||
pathParts: [Comment], | ||
cursorIndentDepth: -1, | ||
}; | ||
} | ||
} | ||
if ((result = ItemKeyValueRegex.exec(currentLine))) { | ||
@@ -243,3 +254,3 @@ // First, see if it's an ItemKeyValue, i.e. ` - foo: bar` | ||
} | ||
else if (params.position.character > indentLength) { | ||
else if (params.position.character >= indentLength) { | ||
// If the position is after the indent, we're in the key | ||
@@ -277,3 +288,3 @@ pathParts.unshift(keyName); | ||
cursorIndentDepth = indentLength / tabSize; | ||
if (params.position.character > indentLength) { | ||
if (params.position.character >= indentLength) { | ||
// If the position is after the indent, we're in the value | ||
@@ -322,2 +333,6 @@ pathParts.unshift(Value); | ||
const WhitespaceRegex = /^(?<indent> *)$/im; | ||
// A regex for matching any line with a comment (or if the whole line is a comment) | ||
// TODO: This will match a 'string with a #', which is *not* a comment, but currently this will not affect any end scenarios | ||
// TODO: The closest I've gotten is /(['"])?.*(?<commentSep>#)(?:(?!\1).)*$/im | ||
const LineWithCommentRegex = /^.*(?<commentSep>#).*$/im; | ||
// Constants for marking non-key parts of a logical path | ||
@@ -327,2 +342,3 @@ const Value = '<value>'; | ||
const Sep = '<sep>'; | ||
const Comment = '<comment>'; | ||
//# sourceMappingURL=ComposeDocument.js.map |
@@ -21,3 +21,3 @@ "use strict"; | ||
constructor(connection, clientParams) { | ||
var _a; | ||
var _a, _b; | ||
this.connection = connection; | ||
@@ -28,3 +28,3 @@ this.clientParams = clientParams; | ||
// Hook up the document listeners, which create a Disposable which will be added to this.subscriptions | ||
this.createDocumentManagerHandler(this.documentManager.onDidChangeContent, new DiagnosticProvider_1.DiagnosticProvider().on); | ||
this.createDocumentManagerHandler(this.documentManager.onDidChangeContent, new DiagnosticProvider_1.DiagnosticProvider((_a = clientParams.initializationOptions) === null || _a === void 0 ? void 0 : _a.diagnosticDelay)); | ||
// Hook up all the LSP listeners, which do not create Disposables for some reason | ||
@@ -40,3 +40,3 @@ this.createLspHandler(this.connection.onCompletion, new MultiCompletionProvider_1.MultiCompletionProvider()); | ||
// Start the telemetry aggregator | ||
this.subscriptions.push(this.telemetryAggregator = new TelemetryAggregator_1.TelemetryAggregator(this.connection, (_a = clientParams.initializationOptions) === null || _a === void 0 ? void 0 : _a.telemetryAggregationInterval)); | ||
this.subscriptions.push(this.telemetryAggregator = new TelemetryAggregator_1.TelemetryAggregator(this.connection, (_b = clientParams.initializationOptions) === null || _b === void 0 ? void 0 : _b.telemetryAggregationInterval)); | ||
} | ||
@@ -97,3 +97,3 @@ dispose() { | ||
event(async (params) => { | ||
return await this.callWithTelemetryAndErrorHandling(handler.name, async () => { | ||
return await this.callWithTelemetryAndErrorHandling(handler.constructor.name, async () => { | ||
const extendedParams = { | ||
@@ -103,3 +103,3 @@ ...params, | ||
}; | ||
return await Promise.resolve(handler(extendedParams)); | ||
return await Promise.resolve(handler.on(extendedParams, vscode_languageserver_1.CancellationToken.None)); | ||
}); | ||
@@ -106,0 +106,0 @@ }, this, this.subscriptions); |
@@ -10,2 +10,3 @@ "use strict"; | ||
const ProviderBase_1 = require("../ProviderBase"); | ||
const PortsCompletionCollection_1 = require("./PortsCompletionCollection"); | ||
const RootCompletionCollection_1 = require("./RootCompletionCollection"); | ||
@@ -26,2 +27,3 @@ const ServiceCompletionCollection_1 = require("./ServiceCompletionCollection"); | ||
VolumesCompletionCollection_1.VolumesCompletionCollection, | ||
PortsCompletionCollection_1.PortsCompletionCollection, | ||
]; | ||
@@ -28,0 +30,0 @@ } |
@@ -8,4 +8,41 @@ "use strict"; | ||
exports.PortsCompletionCollection = void 0; | ||
const vscode_languageserver_1 = require("vscode-languageserver"); | ||
const CompletionCollection_1 = require("./CompletionCollection"); | ||
exports.PortsCompletionCollection = new CompletionCollection_1.CompletionCollection('ports', { logicalPaths: [/foo/i], indentationDepth: 3 }, ...[]); | ||
// Matches ` - ""` or ` -`, with allowances for other amounts of whitespace/quoting | ||
const PortItemStartRegex = /(\s*-\s*)(?<leadingQuote>"|')?\2\s*$/i; | ||
exports.PortsCompletionCollection = new CompletionCollection_1.CompletionCollection('ports', { logicalPaths: [/^\/services\/[.\w-]+\/ports\/<item>\/.*$/i], indentationDepth: 3 }, ...[ | ||
{ | ||
matcher: PortItemStartRegex, | ||
label: 'containerPort', | ||
insertText: '"${1:80}"$0', | ||
insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet, | ||
}, | ||
{ | ||
matcher: PortItemStartRegex, | ||
label: 'hostPort:containerPort', | ||
insertText: '"${1:8080}:${2:80}"$0', | ||
insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet, | ||
}, | ||
{ | ||
matcher: PortItemStartRegex, | ||
label: 'hostPort:containerPort/protocol', | ||
insertText: '"${1:8080}:${2:80}/${3|tcp,udp|}"$0', | ||
insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet, | ||
}, | ||
{ | ||
matcher: PortItemStartRegex, | ||
label: 'hostRange:containerRange', | ||
insertText: '"${1:8080}-${2:8081}:${3:80}-${4:81}"$0', | ||
insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet, | ||
}, | ||
{ | ||
matcher: PortItemStartRegex, | ||
label: '(Long form port specification)', | ||
insertText: 'target: ${1:80}\n published: ${2:8080}\n protocol: ${3|tcp,udp|}\n mode: ${4|host,ingress|}$0', | ||
insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet, | ||
insertTextMode: vscode_languageserver_1.InsertTextMode.adjustIndentation, | ||
documentation: 'target: <containerPort>\n published: <hostPort>\n protocol: <udp, tcp>\n mode: <host, ingress>', | ||
sortText: 'zzz', // Force this to sort to the bottom | ||
}, | ||
]); | ||
//# sourceMappingURL=PortsCompletionCollection.js.map |
@@ -124,3 +124,3 @@ "use strict"; | ||
label: 'ports:', | ||
insertText: 'ports:\n - ${1:port}$0', | ||
insertText: 'ports:\n -$0', | ||
insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet, | ||
@@ -131,3 +131,3 @@ insertTextMode: vscode_languageserver_1.InsertTextMode.adjustIndentation, | ||
label: 'volumes:', | ||
insertText: 'volumes:\n - ${1:volume}$0', | ||
insertText: 'volumes:\n -$0', | ||
insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet, | ||
@@ -134,0 +134,0 @@ insertTextMode: vscode_languageserver_1.InsertTextMode.adjustIndentation, |
@@ -10,3 +10,5 @@ /*!-------------------------------------------------------------------------------------------- | ||
export declare class DiagnosticProvider extends ProviderBase<TextDocumentChangeEvent<ComposeDocument> & ExtendedParams, void, never, never> { | ||
private readonly diagnosticDelay; | ||
constructor(diagnosticDelay?: number); | ||
on(params: TextDocumentChangeEvent<ComposeDocument> & ExtendedParams): void; | ||
} |
@@ -13,13 +13,18 @@ "use strict"; | ||
const ProviderBase_1 = require("./ProviderBase"); | ||
// The time between when typing stops and when diagnostics will be sent (milliseconds) | ||
// The default time between when typing stops and when diagnostics will be sent (milliseconds) | ||
const DiagnosticDelay = 1000; | ||
class DiagnosticProvider extends ProviderBase_1.ProviderBase { | ||
constructor(diagnosticDelay = DiagnosticDelay) { | ||
super(); | ||
this.diagnosticDelay = diagnosticDelay; | ||
} | ||
on(params) { | ||
var _a; | ||
const ctx = (0, ActionContext_1.getCurrentContext)(); | ||
ctx.telemetry.suppressAll = true; // Diagnostics is async and telemetry won't really work | ||
ctx.telemetry.properties.isActivationEvent = 'true'; // In case we do someday enable it, let's make sure it's treated as an activation event since it is done automatically | ||
if (!((_a = ctx.clientCapabilities.textDocument) === null || _a === void 0 ? void 0 : _a.publishDiagnostics)) { | ||
return; | ||
} | ||
ctx.telemetry.suppressAll = true; // Diagnostics is async and telemetry won't really work | ||
(0, debounce_1.debounce)(DiagnosticDelay, { uri: params.document.textDocument.uri, callId: 'diagnostics' }, () => { | ||
(0, debounce_1.debounce)(this.diagnosticDelay, { uri: params.document.textDocument.uri, callId: 'diagnostics' }, () => { | ||
const diagnostics = []; | ||
@@ -26,0 +31,0 @@ for (const error of [...params.document.yamlDocument.value.errors, ...params.document.yamlDocument.value.warnings]) { |
@@ -21,3 +21,4 @@ "use strict"; | ||
}; | ||
const range = vscode_languageserver_1.Range.create(params.document.textDocument.positionAt(0), params.document.textDocument.positionAt(params.document.textDocument.getText().length - 1)); | ||
const range = vscode_languageserver_1.Range.create(params.document.textDocument.positionAt(0), params.document.textDocument.positionAt(params.document.textDocument.getText().length) // This technically goes past the end of the doc, but it's OK because the protocol accepts this (positions past the end of the doc are rounded backward) | ||
); | ||
const formatted = params.document.yamlDocument.value.toString(options); | ||
@@ -24,0 +25,0 @@ // It's heavy-handed but the replacement is for the entire document |
@@ -19,2 +19,3 @@ "use strict"; | ||
const ctx = (0, ActionContext_1.getCurrentContext)(); | ||
ctx.telemetry.properties.isActivationEvent = 'true'; // This happens automatically so we'll treat it as isActivationEvent === true | ||
const results = []; | ||
@@ -21,0 +22,0 @@ const imageTypes = new Set(); |
@@ -20,10 +20,18 @@ "use strict"; | ||
const positionInfo = await params.document.getPositionInfo(params); | ||
const keyInfo = ComposeKeyInfo.find((k) => k.pathRegex.test(positionInfo.path)); | ||
if (keyInfo) { | ||
for (const keyInfo of ComposeKeyInfo) { | ||
const pathMatch = keyInfo.pathRegex.exec(positionInfo.path); | ||
if (!pathMatch) { | ||
continue; | ||
} | ||
const line = params.document.lineAt(params.position); | ||
const match = ComposeDocument_1.KeyValueRegex.exec(line); | ||
const keyName = (_c = match === null || match === void 0 ? void 0 : match.groups) === null || _c === void 0 ? void 0 : _c['keyName']; | ||
if (keyName) { | ||
const keyIndex = line.indexOf(keyName); | ||
ctx.telemetry.properties.hoverMatch = keyInfo.pathRegex.source; | ||
const lineMatch = ComposeDocument_1.KeyValueRegex.exec(line); | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const pathKeyName = pathMatch.groups['keyName']; // Can't be undefined if it matched | ||
const lineKeyName = (_c = lineMatch === null || lineMatch === void 0 ? void 0 : lineMatch.groups) === null || _c === void 0 ? void 0 : _c['keyName']; | ||
// Need to ensure the key on the line is the same as the key in the path | ||
// They can be different is because if you are in the whitespace before a key--the path will be in the parent key, but no hover should be provided here | ||
if (lineKeyName === pathKeyName) { | ||
const keyIndex = line.indexOf(lineKeyName); | ||
// Attach the key name to telemetry | ||
ctx.telemetry.properties.keyName = lineKeyName; | ||
return { | ||
@@ -34,3 +42,3 @@ contents: { | ||
}, | ||
range: vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(params.position.line, keyIndex), vscode_languageserver_1.Position.create(params.position.line, keyIndex + keyName.length)), | ||
range: vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(params.position.line, keyIndex), vscode_languageserver_1.Position.create(params.position.line, keyIndex + lineKeyName.length)), | ||
}; | ||
@@ -45,119 +53,119 @@ } | ||
{ | ||
pathRegex: /^\/configs$/i, | ||
pathRegex: /^\/(?<keyName>configs)$/i, | ||
plaintextContents: 'Configurations for services in the project', | ||
}, | ||
{ | ||
pathRegex: /^\/networks$/i, | ||
pathRegex: /^\/(?<keyName>networks)$/i, | ||
plaintextContents: 'Networks that are shared among multiple services', | ||
}, | ||
{ | ||
pathRegex: /^\/networks\/[.\w-]+\/driver$/i, | ||
pathRegex: /^\/networks\/[.\w-]+\/(?<keyName>driver)$/i, | ||
plaintextContents: 'The driver used for this network', | ||
}, | ||
{ | ||
pathRegex: /^\/secrets$/i, | ||
pathRegex: /^\/(?<keyName>secrets)$/i, | ||
plaintextContents: 'Secrets that are shared among multiple services', | ||
}, | ||
{ | ||
pathRegex: /^\/services$/i, | ||
pathRegex: /^\/(?<keyName>services)$/i, | ||
plaintextContents: 'The services in your project', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/build$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>build)$/i, | ||
plaintextContents: 'The context used for building the image', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/build\/args$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/build\/(?<keyName>args)$/i, | ||
plaintextContents: 'Arguments used during the image build process', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/build\/context$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/build\/(?<keyName>context)$/i, | ||
plaintextContents: 'The context used for building the image', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/build\/dockerfile$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/build\/(?<keyName>dockerfile)$/i, | ||
plaintextContents: 'The Dockerfile used for building the image', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/command$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>command)$/i, | ||
plaintextContents: 'The command that will be run in the container', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/container_name$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>container_name)$/i, | ||
plaintextContents: 'The name that will be given to the container', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/depends_on$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>depends_on)$/i, | ||
plaintextContents: 'Other services that this service depends on, which will be started before this one', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/entrypoint$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>entrypoint)$/i, | ||
plaintextContents: 'The entrypoint to the application in the container', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/env_file$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>env_file)$/i, | ||
plaintextContents: 'Files containing environment variables that will be included', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/environment$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>environment)$/i, | ||
plaintextContents: 'Environment variables that will be included', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/expose$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>expose)$/i, | ||
plaintextContents: 'Ports exposed to the other services but not to the host machine', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/healthcheck$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>healthcheck)$/i, | ||
plaintextContents: 'A command for checking if the container is healthy', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/image$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>image)$/i, | ||
plaintextContents: 'The image that will be pulled for the service. If `build` is specified, the built image will be given this tag.', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/labels$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>labels)$/i, | ||
plaintextContents: 'Labels that will be given to the container', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/logging$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>logging)$/i, | ||
plaintextContents: 'Settings for logging for this service', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/networks$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>networks)$/i, | ||
plaintextContents: 'The service will be included in these networks, allowing it to reach other containers on the same network', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/ports$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>ports)$/i, | ||
plaintextContents: 'Ports that will be exposed to the host', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/profiles$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>profiles)$/i, | ||
plaintextContents: 'Profiles that this service is a part of. When the profile is started, this service will be started.', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/secrets$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>secrets)$/i, | ||
plaintextContents: 'Secrets the service will have access to', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/user$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>user)$/i, | ||
plaintextContents: 'The username under which the app in the container will be started', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/volumes$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>volumes)$/i, | ||
plaintextContents: 'Named volumes and paths on the host mapped to paths in the container', | ||
}, | ||
{ | ||
pathRegex: /^\/services\/[.\w-]+\/working_dir$/i, | ||
pathRegex: /^\/services\/[.\w-]+\/(?<keyName>working_dir)$/i, | ||
plaintextContents: 'The working directory in which the entrypoint or command will be run', | ||
}, | ||
{ | ||
pathRegex: /^\/version$/i, | ||
pathRegex: /^\/(?<keyName>version)$/i, | ||
plaintextContents: 'The version of the Docker Compose document', | ||
}, | ||
{ | ||
pathRegex: /^\/volumes$/i, | ||
pathRegex: /^\/(?<keyName>volumes)$/i, | ||
plaintextContents: 'Named volumes that are shared among multiple services', | ||
}, | ||
{ | ||
pathRegex: /^\/volumes\/[.\w-]+\/driver$/i, | ||
pathRegex: /^\/volumes\/[.\w-]+\/(?<keyName>driver)$/i, | ||
plaintextContents: 'The driver used for this volume', | ||
@@ -164,0 +172,0 @@ }, |
@@ -7,3 +7,3 @@ /*!-------------------------------------------------------------------------------------------- | ||
export declare abstract class ProviderBase<P, R, PR, E> { | ||
abstract on(params: P, token: CancellationToken, workDoneProgress: WorkDoneProgressReporter, resultProgress?: ResultProgressReporter<PR>): HandlerResult<R, E>; | ||
abstract on(params: P, token: CancellationToken, workDoneProgress?: WorkDoneProgressReporter, resultProgress?: ResultProgressReporter<PR>): HandlerResult<R, E>; | ||
} |
@@ -10,2 +10,2 @@ /*!-------------------------------------------------------------------------------------------- | ||
} | ||
export declare function debounce(delay: number, id: DebounceId, callback: () => Promise<void> | void, thisArg: unknown): void; | ||
export declare function debounce(delay: number, id: DebounceId, callback: () => Promise<void> | void, thisArg?: unknown): void; |
@@ -8,2 +8,3 @@ /*!-------------------------------------------------------------------------------------------- | ||
* Lognormal is a decent approximation of performance (i.e. durations of things). | ||
* Fun fact! Compose file line counts are very well-fit by a lognormal distribution, mu=3.7970, sigma=1.0152 | ||
* @see https://en.wikipedia.org/wiki/Log-normal_distribution | ||
@@ -10,0 +11,0 @@ * @param values The list of values to fit a lognormal distribution to |
@@ -11,2 +11,3 @@ "use strict"; | ||
* Lognormal is a decent approximation of performance (i.e. durations of things). | ||
* Fun fact! Compose file line counts are very well-fit by a lognormal distribution, mu=3.7970, sigma=1.0152 | ||
* @see https://en.wikipedia.org/wiki/Log-normal_distribution | ||
@@ -18,4 +19,13 @@ * @param values The list of values to fit a lognormal distribution to | ||
if (!(values === null || values === void 0 ? void 0 : values.length)) { | ||
return { mu: 0, sigma: 0, median: 0 }; | ||
// If there's no elements, return all 0's | ||
return { mu: 0, sigma: 0, median: 0, }; | ||
} | ||
else if (values.length === 1) { | ||
// If there's only 1 element sigma must be 0 | ||
return { | ||
median: round(values[0], 3), | ||
mu: round(ln(values[0]), 3), | ||
sigma: 0, | ||
}; | ||
} | ||
const n = values.length; | ||
@@ -27,11 +37,4 @@ const lnValues = values.map(a => ln(a)); | ||
// Sigma is calculated from the natural logs and their squares | ||
// If there's only 1 element sigma must be 0 | ||
let sigma; | ||
if (n > 1) { | ||
sigma = Math.sqrt((n * sum(sqLnValues) - sq(sum(lnValues))) / | ||
(n * (n - 1))); | ||
} | ||
else { | ||
sigma = 0; | ||
} | ||
const sigma = Math.sqrt((n * sum(sqLnValues) - sq(sum(lnValues))) / | ||
(n * (n - 1))); | ||
return { | ||
@@ -38,0 +41,0 @@ mu: round(mu, 3), |
{ | ||
"name": "@microsoft/compose-language-service", | ||
"author": "Microsoft Corporation", | ||
"version": "0.0.2-alpha", | ||
"version": "0.0.3-alpha", | ||
"publisher": "ms-azuretools", | ||
@@ -27,3 +27,4 @@ "description": "Language service for Docker Compose documents", | ||
"pretest": "npm run build", | ||
"prepack": "npm run build" | ||
"prepack": "npm run build", | ||
"postinstall": "cd ./src/test/clientExtension && npm install" | ||
}, | ||
@@ -30,0 +31,0 @@ "devDependencies": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Install scripts
Supply chain riskInstall scripts are run when the package is installed. The majority of malware in npm is hidden in install scripts.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
170542
1898
0
1