item);
110 | }
111 | }),
112 | input.onDidChangeSelection(items => resolve(items[0])),
113 | input.onDidHide(() => {
114 | (async () => {
115 | reject(shouldResume && await shouldResume() ? InputFlowAction.resume : InputFlowAction.cancel);
116 | })()
117 | .catch(reject);
118 | })
119 | );
120 | if (this.current) {
121 | this.current.dispose();
122 | }
123 | this.current = input;
124 | this.current.show();
125 | });
126 | } finally {
127 | disposables.forEach(d => d.dispose());
128 | }
129 | }
130 |
131 | async showInputBox({ title, step, totalSteps, value, prompt, validate, buttons, ignoreFocusOut, placeholder, shouldResume }: P) {
132 | const disposables: Disposable[] = [];
133 | try {
134 | return await new Promise((resolve, reject) => {
135 | const input = window.createInputBox();
136 | input.title = title;
137 | input.step = step;
138 | input.totalSteps = totalSteps;
139 | input.value = value || '';
140 | input.prompt = prompt;
141 | input.ignoreFocusOut = ignoreFocusOut ?? false;
142 | input.placeholder = placeholder;
143 | input.buttons = [
144 | ...(this.steps.length > 1 ? [QuickInputButtons.Back] : []),
145 | ...(buttons || [])
146 | ];
147 | let validating = validate('');
148 | disposables.push(
149 | input.onDidTriggerButton(item => {
150 | if (item === QuickInputButtons.Back) {
151 | reject(InputFlowAction.back);
152 | } else {
153 | resolve(item);
154 | }
155 | }),
156 | input.onDidAccept(async () => {
157 | const value = input.value;
158 | input.enabled = false;
159 | input.busy = true;
160 | if (!(await validate(value))) {
161 | resolve(value);
162 | }
163 | input.enabled = true;
164 | input.busy = false;
165 | }),
166 | input.onDidChangeValue(async text => {
167 | const current = validate(text);
168 | validating = current;
169 | const validationMessage = await current;
170 | if (current === validating) {
171 | input.validationMessage = validationMessage;
172 | }
173 | }),
174 | input.onDidHide(() => {
175 | (async () => {
176 | reject(shouldResume && await shouldResume() ? InputFlowAction.resume : InputFlowAction.cancel);
177 | })()
178 | .catch(reject);
179 | })
180 | );
181 | if (this.current) {
182 | this.current.dispose();
183 | }
184 | this.current = input;
185 | this.current.show();
186 | });
187 | } finally {
188 | disposables.forEach(d => d.dispose());
189 | }
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/src/utils/outputChannel.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | * SPDX-License-Identifier: Apache-2.0
4 | *
5 | * Changes:
6 | * 2023-06-09: Change name of output channel
7 | */
8 | // Source: https://github.com/aws/aws-toolkit-vscode/blob/master/src/shared/logger/outputChannel.ts
9 |
10 | import * as vscode from 'vscode'
11 |
12 | export const logOutputChannel: vscode.OutputChannel = vscode.window.createOutputChannel('LocalStack')
13 |
14 | /**
15 | * Shows the log output channel.
16 | */
17 | export function showLogOutputChannel({ preserveFocus = true }: { preserveFocus?: boolean } = {}): void {
18 | logOutputChannel.show(preserveFocus)
19 | }
20 |
--------------------------------------------------------------------------------
/src/utils/shell.ts:
--------------------------------------------------------------------------------
1 | import * as cp from "child_process";
2 |
3 | // Alternatives:
4 | // a) Use child_process directly: https://stackoverflow.com/a/43008075
5 | // b) Some TerminalWrapper or the Terminal API: https://stackoverflow.com/a/62774501
6 | // c) Convenience childProcess used in AWS Toolkit VS Code extension:
7 | // https://github.com/aws/aws-toolkit-vscode/blob/master/src/shared/utilities/childProcess.ts
8 | // Basic helper to execute shell commands: https://stackoverflow.com/a/64598488
9 | export async function execShell(cmd: string) {
10 | return new Promise((resolve, reject) => {
11 | cp.exec(cmd, (err, out) => {
12 | if (err) {
13 | return reject(err);
14 | }
15 | return resolve(out);
16 | });
17 | });
18 | }
19 |
--------------------------------------------------------------------------------
/src/utils/templateFinder.ts:
--------------------------------------------------------------------------------
1 | import * as vscode from 'vscode';
2 | // import { CloudFormation } from '../cloudformation/cloudformation';
3 | import { glob } from 'glob';
4 |
5 | export async function findCFNTemplates(directoryPath: string): Promise {
6 | const templateCandidates = await findYamlFiles(directoryPath);
7 | const templates: string[] = [];
8 | templateCandidates.forEach(function (candidate) {
9 | // TODO: validate Cfn and add valid ones to templates
10 | templates.push(candidate);
11 | });
12 | return templates;
13 | }
14 |
15 |
16 | // Limitation: could take a long time in directories with many yaml files
17 | // glob: https://github.com/isaacs/node-glob
18 | function findYamlFiles(directoryPath: string): Promise {
19 | return new Promise((resolve, reject) => {
20 | const pattern = '**/*.yaml';
21 | const options = {
22 | cwd: directoryPath,
23 | nodir: true,
24 | ignore: 'node_modules/**',
25 | };
26 |
27 | glob(pattern, options, (err: Error | null, files: string[]) => {
28 | if (err) {
29 | reject(err);
30 | return;
31 | }
32 |
33 | resolve(files);
34 | });
35 | });
36 | }
37 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "ES2020",
5 | "lib": [
6 | "ES2020"
7 | ],
8 | "sourceMap": true,
9 | "rootDir": "src",
10 | "strict": true /* enable all strict type-checking options */
11 | /* Additional Checks */
12 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
13 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
14 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/vsc-extension-quickstart.md:
--------------------------------------------------------------------------------
1 | # Welcome to your VS Code Extension
2 |
3 | ## What's in the folder
4 |
5 | * This folder contains all of the files necessary for your extension.
6 | * `package.json` - this is the manifest file in which you declare your extension and command.
7 | * The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin.
8 | * `src/extension.ts` - this is the main file where you will provide the implementation of your command.
9 | * The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
10 | * We pass the function containing the implementation of the command as the second parameter to `registerCommand`.
11 |
12 | ## Setup
13 |
14 | * install the recommended extensions (amodio.tsl-problem-matcher and dbaeumer.vscode-eslint)
15 |
16 |
17 | ## Get up and running straight away
18 |
19 | * Press `F5` to open a new window with your extension loaded.
20 | * Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`.
21 | * Set breakpoints in your code inside `src/extension.ts` to debug your extension.
22 | * Find output from your extension in the debug console.
23 |
24 | ## Make changes
25 |
26 | * You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`.
27 | * You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
28 |
29 |
30 | ## Explore the API
31 |
32 | * You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`.
33 |
34 | ## Run tests
35 |
36 | * Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`.
37 | * Press `F5` to run the tests in a new window with your extension loaded.
38 | * See the output of the test result in the debug console.
39 | * Make changes to `src/test/suite/extension.test.ts` or create new test files inside the `test/suite` folder.
40 | * The provided test runner will only consider files matching the name pattern `**.test.ts`.
41 | * You can create folders inside the `test` folder to structure your tests any way you want.
42 |
43 | ## Go further
44 |
45 | * Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension).
46 | * [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace.
47 | * Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).
48 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | //@ts-check
2 |
3 | 'use strict';
4 |
5 | const path = require('path');
6 |
7 | //@ts-check
8 | /** @typedef {import('webpack').Configuration} WebpackConfig **/
9 |
10 | /** @type WebpackConfig */
11 | const extensionConfig = {
12 | target: 'node', // VS Code extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/
13 | mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
14 |
15 | entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
16 | output: {
17 | // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/
18 | path: path.resolve(__dirname, 'dist'),
19 | filename: 'extension.js',
20 | libraryTarget: 'commonjs2'
21 | },
22 | externals: {
23 | vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
24 | // modules added here also need to be added in the .vscodeignore file
25 | },
26 | resolve: {
27 | // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
28 | extensions: ['.ts', '.js']
29 | },
30 | module: {
31 | rules: [
32 | {
33 | test: /\.ts$/,
34 | exclude: /node_modules/,
35 | use: [
36 | {
37 | loader: 'ts-loader'
38 | }
39 | ]
40 | }
41 | ]
42 | },
43 | devtool: 'nosources-source-map',
44 | infrastructureLogging: {
45 | level: "log", // enables logging required for problem matchers
46 | },
47 | };
48 | module.exports = [ extensionConfig ];
--------------------------------------------------------------------------------