vscode-eslint 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/$shared/customMessages.ts +113 -0
  2. package/$shared/settings.ts +189 -0
  3. package/.CodeQL.yml +5 -0
  4. package/.azure-pipelines.yml +27 -0
  5. package/.github/commands.yml +127 -0
  6. package/.github/locker.yml +6 -0
  7. package/.github/needs_more_info.yml +6 -0
  8. package/.github/workflows/npm-publish.yml +53 -0
  9. package/.github/workflows/release-please.yml +22 -0
  10. package/.lsifrc.json +4 -0
  11. package/.vscode/launch.json +20 -0
  12. package/.vscode/settings.json +52 -0
  13. package/.vscode/spellright.dict +8 -0
  14. package/.vscode/tasks.json +39 -0
  15. package/.vscodeignore +23 -0
  16. package/CHANGELOG.md +524 -0
  17. package/License.txt +17 -0
  18. package/README.md +517 -0
  19. package/SECURITY.md +41 -0
  20. package/agents.md +36 -0
  21. package/bin/vscode-eslint.js +56 -0
  22. package/build/azure-pipelines/linux/build.yml +14 -0
  23. package/build/azure-pipelines/pre-release.yml +42 -0
  24. package/build/azure-pipelines/release.yml +45 -0
  25. package/build/azure-pipelines/win32/build.yml +14 -0
  26. package/build/bin/all.js +29 -0
  27. package/build/bin/linking.js +102 -0
  28. package/build/bin/symlink.js +35 -0
  29. package/client/.mocharc.json +6 -0
  30. package/client/agents.md +5 -0
  31. package/client/package-lock.json +176 -0
  32. package/client/package.json +29 -0
  33. package/client/src/client.ts +992 -0
  34. package/client/src/extension.ts +180 -0
  35. package/client/src/node-utils.ts +393 -0
  36. package/client/src/settings.ts +379 -0
  37. package/client/src/tasks.ts +186 -0
  38. package/client/src/tests/glob.test.ts +31 -0
  39. package/client/src/vscode-utils.ts +28 -0
  40. package/client/test/mocha.opts +3 -0
  41. package/client/tsconfig.json +20 -0
  42. package/client/webpack.config.js +25 -0
  43. package/contributing.md +19 -0
  44. package/esbuild.js +62 -0
  45. package/eslint.config.js +129 -0
  46. package/eslint_icon.png +0 -0
  47. package/history/settings_1_9_x.md +110 -0
  48. package/images/2_1_10/eslint-dialog.png +0 -0
  49. package/images/2_1_10/eslint-status.png +0 -0
  50. package/package-json-schema.json +9 -0
  51. package/package.json +686 -0
  52. package/playgrounds/7.0/.eslintignore +1 -0
  53. package/playgrounds/7.0/.eslintrc.json +71 -0
  54. package/playgrounds/7.0/.vscode/settings.json +85 -0
  55. package/playgrounds/7.0/app.js +12 -0
  56. package/playgrounds/7.0/build/.eslintignore +1 -0
  57. package/playgrounds/7.0/build/.eslintrc.json +30 -0
  58. package/playgrounds/7.0/build/build.js +11 -0
  59. package/playgrounds/7.0/jsconfig.json +5 -0
  60. package/playgrounds/7.0/package-lock.json +2133 -0
  61. package/playgrounds/7.0/package.json +10 -0
  62. package/playgrounds/7.0/readme.md +0 -0
  63. package/playgrounds/7.0/subDir/sub.js +11 -0
  64. package/playgrounds/7.0/subDir/test.jsx +10 -0
  65. package/playgrounds/7.0/test.js +11 -0
  66. package/playgrounds/7.0/test.sh +1 -0
  67. package/playgrounds/7.0/test.vue +33 -0
  68. package/playgrounds/7.0/test2.html +8 -0
  69. package/playgrounds/8.0/.eslintignore +1 -0
  70. package/playgrounds/8.0/.eslintrc.json +71 -0
  71. package/playgrounds/8.0/.vscode/settings.json +91 -0
  72. package/playgrounds/8.0/app.js +12 -0
  73. package/playgrounds/8.0/build/.eslintignore +1 -0
  74. package/playgrounds/8.0/build/.eslintrc.json +30 -0
  75. package/playgrounds/8.0/build/build.js +11 -0
  76. package/playgrounds/8.0/jsconfig.json +5 -0
  77. package/playgrounds/8.0/package-lock.json +2321 -0
  78. package/playgrounds/8.0/package.json +10 -0
  79. package/playgrounds/8.0/readme.md +17 -0
  80. package/playgrounds/8.0/subDir/sub.js +11 -0
  81. package/playgrounds/8.0/subDir/test.jsx +10 -0
  82. package/playgrounds/8.0/test.ipynb +49 -0
  83. package/playgrounds/8.0/test.js +3 -0
  84. package/playgrounds/8.0/test.sh +1 -0
  85. package/playgrounds/8.0/test.vue +33 -0
  86. package/playgrounds/8.0/test2.html +8 -0
  87. package/playgrounds/9.0/flat/.vscode/settings.json +3 -0
  88. package/playgrounds/9.0/flat/app.js +12 -0
  89. package/playgrounds/9.0/flat/dist/ignore.js +12 -0
  90. package/playgrounds/9.0/flat/eslint.config.js +61 -0
  91. package/playgrounds/9.0/flat/package-lock.json +1053 -0
  92. package/playgrounds/9.0/flat/package.json +9 -0
  93. package/playgrounds/9.0/rc/.eslintrc.json +57 -0
  94. package/playgrounds/9.0/rc/.vscode/settings.json +3 -0
  95. package/playgrounds/9.0/rc/app.js +12 -0
  96. package/playgrounds/9.0/rc/package-lock.json +1345 -0
  97. package/playgrounds/9.0/rc/package.json +9 -0
  98. package/playgrounds/flat-config/.vscode/settings.json +22 -0
  99. package/playgrounds/flat-config/app.js +12 -0
  100. package/playgrounds/flat-config/eslint.config.js +51 -0
  101. package/playgrounds/flat-config/package-lock.json +2733 -0
  102. package/playgrounds/flat-config/package.json +12 -0
  103. package/playgrounds/flat-config/sub/sub.js +2 -0
  104. package/playgrounds/flat-config/test.ts +7 -0
  105. package/playgrounds/flat-config/tsconfig.json +11 -0
  106. package/playgrounds/flat-config-fail/f1/app.js +12 -0
  107. package/playgrounds/flat-config-fail/f1/eslint.config.js +51 -0
  108. package/playgrounds/flat-config-fail/package-lock.json +1683 -0
  109. package/playgrounds/flat-config-fail/package.json +11 -0
  110. package/playgrounds/flat-config-mjs/.vscode/settings.json +21 -0
  111. package/playgrounds/flat-config-mjs/app.js +12 -0
  112. package/playgrounds/flat-config-mjs/eslint.config.mjs +53 -0
  113. package/playgrounds/flat-config-mjs/package-lock.json +2860 -0
  114. package/playgrounds/flat-config-mjs/package.json +11 -0
  115. package/playgrounds/flat-config-mjs/sub/sub.js +2 -0
  116. package/playgrounds/flat-config-mjs/test.ts +7 -0
  117. package/playgrounds/flat-config-mjs/tsconfig.json +11 -0
  118. package/playgrounds/load-eslint/.vscode/settings.json +21 -0
  119. package/playgrounds/load-eslint/app.js +12 -0
  120. package/playgrounds/load-eslint/eslint.config.js +51 -0
  121. package/playgrounds/load-eslint/package-lock.json +2860 -0
  122. package/playgrounds/load-eslint/package.json +11 -0
  123. package/playgrounds/load-eslint/sub/sub.js +2 -0
  124. package/playgrounds/load-eslint/test.ts +7 -0
  125. package/playgrounds/load-eslint/tsconfig.json +11 -0
  126. package/playgrounds/noLib/test.js +22 -0
  127. package/playgrounds/noWD/.vscode/settings.json +2 -0
  128. package/playgrounds/noWD/src/.eslintrc.json +18 -0
  129. package/playgrounds/noWD/src/package-lock.json +2812 -0
  130. package/playgrounds/noWD/src/package.json +12 -0
  131. package/playgrounds/noWD/src/test.js +3 -0
  132. package/playgrounds/notebooks/notebook.ipynb +7072 -0
  133. package/playgrounds/notebooks/notebook2.ipynb +20 -0
  134. package/playgrounds/testing.code-workspace +28 -0
  135. package/playgrounds/ts/.eslintrc.base.json +23 -0
  136. package/playgrounds/ts/.eslintrc.json +191 -0
  137. package/playgrounds/ts/.vscode/settings.json +12 -0
  138. package/playgrounds/ts/package-lock.json +2687 -0
  139. package/playgrounds/ts/package.json +11 -0
  140. package/playgrounds/ts/test copy.ts +4 -0
  141. package/playgrounds/ts/test.ipynb +49 -0
  142. package/playgrounds/ts/test.ts +4 -0
  143. package/playgrounds/ts/test.tsx +14 -0
  144. package/playgrounds/ts/tsconfig.json +100 -0
  145. package/server/agents.md +9 -0
  146. package/server/package-lock.json +93 -0
  147. package/server/package.json +32 -0
  148. package/server/src/diff.ts +1079 -0
  149. package/server/src/eslint.ts +1471 -0
  150. package/server/src/eslintServer.ts +865 -0
  151. package/server/src/is.ts +18 -0
  152. package/server/src/languageDefaults.ts +40 -0
  153. package/server/src/linkedMap.ts +448 -0
  154. package/server/src/paths.ts +128 -0
  155. package/server/src/thenable.d.ts +5 -0
  156. package/server/tsconfig.json +21 -0
  157. package/server/webpack.config.js +25 -0
  158. package/shared.webpack.config.js +59 -0
  159. package/tsconfig.base.json +9 -0
  160. package/tsconfig.json +21 -0
@@ -0,0 +1,180 @@
1
+ /* --------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ * ------------------------------------------------------------------------------------------ */
5
+
6
+ import * as path from 'path';
7
+ import * as fs from 'fs';
8
+ import {
9
+ workspace as Workspace, window as Window, commands as Commands, Disposable, ExtensionContext, TextDocument
10
+ } from 'vscode';
11
+
12
+ import {
13
+ DocumentDiagnosticRequest,
14
+ LanguageClient,
15
+ State
16
+ } from 'vscode-languageclient/node';
17
+
18
+ import { Validate } from './shared/settings';
19
+
20
+ import { findEslint } from './node-utils';
21
+ import { pickFolder } from './vscode-utils';
22
+ import { TaskProvider } from './tasks';
23
+ import { ESLintClient, Validator } from './client';
24
+
25
+ function createDefaultConfiguration(): void {
26
+ const folders = Workspace.workspaceFolders;
27
+ if (!folders) {
28
+ void Window.showErrorMessage('An ESLint configuration can only be generated if VS Code is opened on a workspace folder.');
29
+ return;
30
+ }
31
+ const noConfigFolders = folders.filter(folder => {
32
+ const configFiles = ['.eslintrc.js', '.eslintrc.cjs', '.eslintrc.yaml', '.eslintrc.yml', '.eslintrc', '.eslintrc.json'];
33
+ for (const configFile of configFiles) {
34
+ if (fs.existsSync(path.join(folder.uri.fsPath, configFile))) {
35
+ return false;
36
+ }
37
+ }
38
+ return true;
39
+ });
40
+ if (noConfigFolders.length === 0) {
41
+ if (folders.length === 1) {
42
+ void Window.showInformationMessage('The workspace already contains an ESLint configuration file.');
43
+ } else {
44
+ void Window.showInformationMessage('All workspace folders already contain an ESLint configuration file.');
45
+ }
46
+ return;
47
+ }
48
+ void pickFolder(noConfigFolders, 'Select a workspace folder to generate a ESLint configuration for').then(async (folder) => {
49
+ if (!folder) {
50
+ return;
51
+ }
52
+ const folderRootPath = folder.uri.fsPath;
53
+ const terminal = Window.createTerminal({
54
+ name: `ESLint init`,
55
+ cwd: folderRootPath
56
+ });
57
+ const eslintCommand = await findEslint(folderRootPath);
58
+ terminal.sendText(`${eslintCommand} --init`);
59
+ terminal.show();
60
+ });
61
+ }
62
+
63
+ let onActivateCommands: Disposable[] | undefined;
64
+ let client: LanguageClient;
65
+ let acknowledgePerformanceStatus: () => void;
66
+ const taskProvider: TaskProvider = new TaskProvider();
67
+ const validator: Validator = new Validator();
68
+
69
+ export function activate(context: ExtensionContext) {
70
+
71
+ function didOpenTextDocument(textDocument: TextDocument) {
72
+ if (activated) {
73
+ return;
74
+ }
75
+ if (validator.check(textDocument) !== Validate.off) {
76
+ openListener.dispose();
77
+ configurationListener.dispose();
78
+ activated = true;
79
+ realActivate(context);
80
+ }
81
+ }
82
+
83
+ function configurationChanged() {
84
+ if (activated) {
85
+ return;
86
+ }
87
+ for (const textDocument of Workspace.textDocuments) {
88
+ if (validator.check(textDocument) !== Validate.off) {
89
+ openListener.dispose();
90
+ configurationListener.dispose();
91
+ activated = true;
92
+ realActivate(context);
93
+ return;
94
+ }
95
+ }
96
+ }
97
+
98
+ let activated: boolean = false;
99
+ const openListener: Disposable = Workspace.onDidOpenTextDocument(didOpenTextDocument);
100
+ const configurationListener: Disposable = Workspace.onDidChangeConfiguration(configurationChanged);
101
+
102
+ const notValidating = () => {
103
+ const enabled = Workspace.getConfiguration('eslint', Window.activeTextEditor?.document).get('enable', true);
104
+ if (!enabled) {
105
+ void Window.showInformationMessage(`ESLint is not running because the deprecated setting 'eslint.enable' is set to false. Remove the setting and use the extension disablement feature.`);
106
+ } else {
107
+ void Window.showInformationMessage('ESLint is not running. By default only TypeScript and JavaScript files are validated. If you want to validate other file types please specify them in the \'eslint.probe\' setting.');
108
+ }
109
+ };
110
+ onActivateCommands = [
111
+ Commands.registerCommand('eslint.executeAutofix', notValidating),
112
+ Commands.registerCommand('eslint.showOutputChannel', notValidating),
113
+ Commands.registerCommand('eslint.migrateSettings', notValidating),
114
+ Commands.registerCommand('eslint.restart', notValidating),
115
+ Commands.registerCommand('eslint.revalidate', notValidating)
116
+ ];
117
+
118
+ context.subscriptions.push(
119
+ Commands.registerCommand('eslint.createConfig', createDefaultConfiguration)
120
+ );
121
+
122
+ taskProvider.start();
123
+ configurationChanged();
124
+ }
125
+
126
+ function realActivate(context: ExtensionContext): void {
127
+
128
+ if (onActivateCommands) {
129
+ onActivateCommands.forEach(command => command.dispose());
130
+ onActivateCommands = undefined;
131
+ }
132
+
133
+ [client, acknowledgePerformanceStatus] = ESLintClient.create(context, validator);
134
+
135
+ context.subscriptions.push(
136
+ Commands.registerCommand('eslint.showOutputChannel', async () => {
137
+ client.outputChannel.show();
138
+ acknowledgePerformanceStatus();
139
+ }),
140
+ Commands.registerCommand('eslint.migrateSettings', () => {
141
+ void ESLintClient.migrateSettings(client);
142
+ }),
143
+ Commands.registerCommand('eslint.restart', async () => {
144
+ // If the previous start failed, we need to create a new client to pick up
145
+ // a potentially updated environment (e.g., PATH changes from direnv).
146
+ if (client.state === State.StartFailed) {
147
+ await client.dispose();
148
+ [client, acknowledgePerformanceStatus] = ESLintClient.create(context, validator);
149
+ return client.start().catch((error) => {
150
+ client.error(`Starting the server failed.`, error, 'force');
151
+ const message = typeof error === 'string' ? error : typeof error.message === 'string' ? error.message : undefined;
152
+ if (message !== undefined && message.indexOf('ENOENT') !== -1) {
153
+ client.info(`PATH environment variable is: ${process.env['PATH']}`);
154
+ }
155
+ });
156
+ } else {
157
+ return client.restart().catch((error) => client.error(`Restarting client failed`, error, 'force'));
158
+ }
159
+ }),
160
+ Commands.registerCommand('eslint.revalidate', () => {
161
+ client.getFeature(DocumentDiagnosticRequest.method).refresh();
162
+ })
163
+ );
164
+
165
+ client.start().catch((error) => {
166
+ client.error(`Starting the server failed.`, error, 'force');
167
+ const message = typeof error === 'string' ? error : typeof error.message === 'string' ? error.message : undefined;
168
+ if (message !== undefined && message.indexOf('ENOENT') !== -1) {
169
+ client.info(`PATH environment variable is: ${process.env['PATH']}`);
170
+ }
171
+ });
172
+ }
173
+
174
+ export function deactivate(): Promise<void> {
175
+ if (onActivateCommands !== undefined) {
176
+ onActivateCommands.forEach(command => command.dispose());
177
+ }
178
+ taskProvider.dispose();
179
+ return client !== undefined ? client.stop() : Promise.resolve();
180
+ }
@@ -0,0 +1,393 @@
1
+ /* --------------------------------------------------------------------------------------------
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License. See License.txt in the project root for license information.
4
+ * ------------------------------------------------------------------------------------------ */
5
+
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+
9
+ export namespace Is {
10
+ const toString = Object.prototype.toString;
11
+
12
+ export function boolean(value: any): value is boolean {
13
+ return value === true || value === false;
14
+ }
15
+
16
+ export function string(value: any): value is string {
17
+ return toString.call(value) === '[object String]';
18
+ }
19
+
20
+ export function objectLiteral(value: any): value is object {
21
+ return value !== null && value !== undefined && !Array.isArray(value) && typeof value === 'object';
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Converts a path to the OS representation,
27
+ * @param path the path to convert.
28
+ * @returns the converted path.
29
+ */
30
+ export function toOSPath(path: string): string {
31
+ if (process.platform === 'win32') {
32
+ path = path.replace(/^\/(\w)\//, '$1:\\');
33
+ return path.replace(/\//g, '\\');
34
+ } else {
35
+ return path;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Converts a path to a posix path.
41
+ * @param path the path to convert.
42
+ * @returns the posix path.
43
+ */
44
+ export function toPosixPath(path: string): string {
45
+ if (process.platform !== 'win32') {
46
+ return path;
47
+ }
48
+ return path.replace(/\\/g, '/');
49
+ }
50
+
51
+ /**
52
+ * Find a ESLint installation at a given root path.
53
+ * @param rootPath the root path.
54
+ * @returns the eslint installation or the unresolved command eslint.
55
+ */
56
+ export async function findEslint(rootPath: string): Promise<string> {
57
+ const platform = process.platform;
58
+ if (platform === 'win32' && await existFile(path.join(rootPath, 'node_modules', '.bin', 'eslint.cmd'))) {
59
+ return path.join('.', 'node_modules', '.bin', 'eslint.cmd');
60
+ } else if ((platform === 'linux' || platform === 'darwin') && await existFile(path.join(rootPath, 'node_modules', '.bin', 'eslint'))) {
61
+ return path.join('.', 'node_modules', '.bin', 'eslint');
62
+ } else {
63
+ return 'eslint';
64
+ }
65
+ }
66
+
67
+ function existFile(file: string): Promise<boolean> {
68
+ return new Promise<boolean>((resolve, _reject) => {
69
+ fs.stat(file, (error, stats) => {
70
+ if (error !== null) {
71
+ resolve(false);
72
+ } else {
73
+ resolve(stats.isFile());
74
+ }
75
+ });
76
+ });
77
+ }
78
+
79
+ // ----- Glob pattern parser
80
+
81
+ enum NodeType {
82
+ text = 'text',
83
+ separator = 'separator',
84
+ brace = 'brace',
85
+ bracket = 'bracket',
86
+ questionMark = 'questionMark',
87
+ star = 'star',
88
+ globStar = 'globStar'
89
+ }
90
+
91
+ // Text inside the pattern
92
+ type TextNode = {
93
+ type: NodeType.text;
94
+ value: string;
95
+ };
96
+
97
+ // A separator
98
+ type SeparatorNode = {
99
+ type: NodeType.separator;
100
+ };
101
+
102
+ // The character ?
103
+ type QuestionMarkNode = {
104
+ type: NodeType.questionMark;
105
+ };
106
+
107
+ // The character *
108
+ type StarNode = {
109
+ type: NodeType.star;
110
+ };
111
+
112
+ // The sequence **
113
+ type GlobStarNode = {
114
+ type: NodeType.globStar;
115
+ };
116
+
117
+ // A bracket
118
+ type BracketNode = {
119
+ type: NodeType.bracket;
120
+ value: string;
121
+ };
122
+
123
+ type BraceAlternative = TextNode | QuestionMarkNode | StarNode | BracketNode | BraceNode;
124
+ type BraceNode = {
125
+ type: NodeType.brace;
126
+ alternatives: BraceAlternative[];
127
+ };
128
+
129
+ type Node = TextNode | SeparatorNode | QuestionMarkNode | StarNode | GlobStarNode | BracketNode | BraceNode;
130
+
131
+ function escapeRegExpCharacters(value: string): string {
132
+ return value.replace(/[\\\{\}\*\+\?\|\^\$\.\[\]\(\)]/g, '\\$&');
133
+ }
134
+
135
+ /**
136
+ * A parser to parse a pattern in string form.
137
+ */
138
+ class PatternParser {
139
+
140
+ private value: string;
141
+ private index: number;
142
+
143
+ private mode: 'pattern' | 'brace';
144
+ private stopChar: string | undefined;
145
+
146
+ constructor(value: string, mode: 'pattern' | 'brace' = 'pattern') {
147
+ this.value = value;
148
+ this.index = 0;
149
+ this.mode = mode;
150
+ this.stopChar = mode === 'pattern' ? undefined : '}';
151
+ }
152
+
153
+ private makeTextNode(start: number): Node {
154
+ return { type: NodeType.text, value: escapeRegExpCharacters(this.value.substring(start, this.index)) };
155
+ }
156
+
157
+ next(): Node | undefined {
158
+ const start = this.index;
159
+ let ch: string | undefined;
160
+ while((ch = this.value[this.index]) !== this.stopChar) {
161
+ switch (ch) {
162
+ case '/':
163
+ if (start < this.index) {
164
+ return this.makeTextNode(start);
165
+ } else {
166
+ this.index++;
167
+ return { type: NodeType.separator };
168
+ }
169
+ case '?':
170
+ this.index++;
171
+ return { type: NodeType.questionMark };
172
+ case '*':
173
+ if (this.value[this.index + 1] === '*') {
174
+ this.index += 2;
175
+ return { type: NodeType.globStar };
176
+ } else {
177
+ this.index++;
178
+ return {type: NodeType.star };
179
+ }
180
+ case '{':
181
+ if (start < this.index) {
182
+ return this.makeTextNode(start);
183
+ } else {
184
+ const bracketParser = new PatternParser(this.value.substring(this.index + 1), 'brace');
185
+ const alternatives: BraceAlternative[] = [];
186
+ let node: Node | undefined;
187
+ while ((node = bracketParser.next()) !== undefined) {
188
+ if (node.type === NodeType.globStar || node.type === NodeType.separator) {
189
+ throw new Error(`Invalid glob pattern ${this.index}. Stopped at ${this.index}`);
190
+ }
191
+ alternatives.push(node);
192
+ }
193
+ this.index= this.index + bracketParser.index + 2;
194
+ return { type: NodeType.brace, alternatives: alternatives };
195
+ }
196
+ break;
197
+ case ',':
198
+ if (this.mode === 'brace') {
199
+ if (start < this.index) {
200
+ const result = this.makeTextNode(start);
201
+ this.index++;
202
+ return result;
203
+ }
204
+ }
205
+ this.index++;
206
+ break;
207
+ case '[':
208
+ // eslint-disable-next-line no-case-declarations
209
+ const buffer: string[] = [];
210
+ this.index++;
211
+ // eslint-disable-next-line no-case-declarations
212
+ const firstIndex = this.index;
213
+ while (this.index < this.value.length) {
214
+ const ch = this.value[this.index];
215
+ if (this.index === firstIndex) {
216
+ switch (ch) {
217
+ case ']':
218
+ buffer.push(ch);
219
+ break;
220
+ case '!':
221
+ case '^':
222
+ buffer.push('^');
223
+ break;
224
+ default:
225
+ buffer.push(escapeRegExpCharacters(ch));
226
+ break;
227
+ }
228
+ } else if (ch === '-') {
229
+ buffer.push(ch);
230
+ } else if (ch === ']') {
231
+ this.index++;
232
+ return { type: NodeType.bracket, value: buffer.join('') };
233
+ } else {
234
+ buffer.push(escapeRegExpCharacters(ch));
235
+ }
236
+ this.index++;
237
+ }
238
+ throw new Error(`Invalid glob pattern ${this.index}. Stopped at ${this.index}`);
239
+ default:
240
+ this.index++;
241
+ }
242
+ }
243
+ return start === this.index ? undefined : this.makeTextNode(start);
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Converts a pattern in string from to a regular expression.
249
+ *
250
+ * @param pattern the pattern as a string to be converted
251
+ * @returns the regular expression or undefined if the pattern can't be converted.
252
+ */
253
+ export function convert2RegExp(pattern: string): RegExp | undefined {
254
+ const separator = process.platform === 'win32' ? '\\\\' : '\\/';
255
+ const fileChar = `[^${separator}]`;
256
+ function convertNode(node: Node): string {
257
+ switch (node.type) {
258
+ case NodeType.separator:
259
+ return separator;
260
+ break;
261
+ case NodeType.text:
262
+ return node.value;
263
+ break;
264
+ case NodeType.questionMark:
265
+ return fileChar;
266
+ break;
267
+ case NodeType.star:
268
+ return `${fileChar}*?`;
269
+ break;
270
+ case NodeType.globStar:
271
+ return `(?:${fileChar}|(?:(?:${fileChar}${separator})+${fileChar}))*?`;
272
+ case NodeType.bracket:
273
+ return `[${node.value}]`;
274
+ case NodeType.brace: {
275
+ const buffer: string[] = [];
276
+ for (const child of node.alternatives) {
277
+ buffer.push(convertNode(child));
278
+ }
279
+ return `(?:${buffer.join('|')})`;
280
+ }
281
+ }
282
+ }
283
+
284
+ try {
285
+ const buffer: string[] = ['^'];
286
+
287
+ const parser = new PatternParser(pattern);
288
+ let node: Node | undefined;
289
+ while ((node = parser.next()) !== undefined) {
290
+ buffer.push(convertNode(node));
291
+ }
292
+ return buffer.length > 0 ? new RegExp(buffer.join('')) : undefined;
293
+ } catch (err) {
294
+ console.error(err);
295
+ return undefined;
296
+ }
297
+ }
298
+
299
+ // ----- semaphore implementation
300
+
301
+ /**
302
+ * A piece of work.
303
+ */
304
+ interface Thunk<T> {
305
+ (): T;
306
+ }
307
+
308
+ /**
309
+ * A thunk waiting in the queue.
310
+ */
311
+ interface Waiting<T> {
312
+ thunk: Thunk<T | PromiseLike<T>>;
313
+ resolve: (value: T | PromiseLike<T>) => void;
314
+ reject: (reason?: any) => void;
315
+ }
316
+
317
+ /**
318
+ * A semaphore implementation.
319
+ */
320
+ export class Semaphore<T = void> {
321
+
322
+ private _capacity: number;
323
+ private _active: number;
324
+ private _waiting: Waiting<T>[];
325
+
326
+ public constructor(capacity: number = 1) {
327
+ if (capacity <= 0) {
328
+ throw new Error('Capacity must be greater than 0');
329
+ }
330
+ this._capacity = capacity;
331
+ this._active = 0;
332
+ this._waiting = [];
333
+ }
334
+
335
+ /**
336
+ * Takes a lock and executes the thunk.
337
+ * @param thunk the thunk to execute
338
+ * @returns a promise for the result of the thunk
339
+ */
340
+ public lock(thunk: () => T | PromiseLike<T>): Promise<T> {
341
+ return new Promise((resolve, reject) => {
342
+ this._waiting.push({ thunk, resolve, reject });
343
+ this.runNext();
344
+ });
345
+ }
346
+
347
+ /**
348
+ * Returns the numbers of the active thunks.
349
+ */
350
+ public get active(): number {
351
+ return this._active;
352
+ }
353
+
354
+ private runNext(): void {
355
+ if (this._waiting.length === 0 || this._active === this._capacity) {
356
+ return;
357
+ }
358
+ setImmediate(() => this.doRunNext());
359
+ }
360
+
361
+ private doRunNext(): void {
362
+ if (this._waiting.length === 0 || this._active === this._capacity) {
363
+ return;
364
+ }
365
+ const next = this._waiting.shift()!;
366
+ this._active++;
367
+ if (this._active > this._capacity) {
368
+ throw new Error(`To many thunks active`);
369
+ }
370
+ try {
371
+ const result = next.thunk();
372
+ if (result instanceof Promise) {
373
+ result.then((value) => {
374
+ this._active--;
375
+ next.resolve(value);
376
+ this.runNext();
377
+ }, (err) => {
378
+ this._active--;
379
+ next.reject(err);
380
+ this.runNext();
381
+ });
382
+ } else {
383
+ this._active--;
384
+ next.resolve(result);
385
+ this.runNext();
386
+ }
387
+ } catch (err) {
388
+ this._active--;
389
+ next.reject(err);
390
+ this.runNext();
391
+ }
392
+ }
393
+ }