mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-15 07:28:05 +00:00
Merge branch 'main' into dev/mjbvz/minimal-sawfish
This commit is contained in:
@@ -166,7 +166,7 @@ const tasks = compilations.map(function (tsconfigFile) {
|
||||
const compileTask = task.define(`compile-extension:${name}`, task.series(cleanTask, async () => {
|
||||
const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'], { dot: true }));
|
||||
const copyNonTs = util.streamToPromise(nonts.pipe(gulp.dest(out)));
|
||||
const tsgo = spawnTsgo(absolutePath, { reporterId: 'extensions' }, () => rewriteTsgoSourceMappingUrlsIfNeeded(false, out, baseUrl));
|
||||
const tsgo = spawnTsgo(absolutePath, { taskName: 'extensions' }, () => rewriteTsgoSourceMappingUrlsIfNeeded(false, out, baseUrl));
|
||||
|
||||
await Promise.all([copyNonTs, tsgo]);
|
||||
}));
|
||||
@@ -175,7 +175,7 @@ const tasks = compilations.map(function (tsconfigFile) {
|
||||
const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'], { dot: true }));
|
||||
const watchInput = watcher(src, { ...srcOpts, ...{ readDelay: 200 } });
|
||||
const watchNonTs = watchInput.pipe(filter(['**', '!**/*.ts'], { dot: true })).pipe(gulp.dest(out));
|
||||
const tsgoStream = watchInput.pipe(util.debounce(() => createTsgoStream(absolutePath, { reporterId: 'extensions' }, () => rewriteTsgoSourceMappingUrlsIfNeeded(false, out, baseUrl)), 200));
|
||||
const tsgoStream = watchInput.pipe(util.debounce(() => createTsgoStream(absolutePath, { taskName: 'extensions' }, () => rewriteTsgoSourceMappingUrlsIfNeeded(false, out, baseUrl)), 200));
|
||||
const watchStream = es.merge(nonts.pipe(gulp.dest(out)), watchNonTs, tsgoStream);
|
||||
|
||||
return watchStream;
|
||||
@@ -276,9 +276,9 @@ gulp.task(watchWebExtensionsTask);
|
||||
async function buildWebExtensions(isWatch: boolean): Promise<void> {
|
||||
const extensionsPath = path.join(root, 'extensions');
|
||||
|
||||
// Find all esbuild-browser.ts files
|
||||
// Find all esbuild.browser.mts files
|
||||
const esbuildConfigLocations = await nodeUtil.promisify(glob)(
|
||||
path.join(extensionsPath, '**', 'esbuild-browser.ts'),
|
||||
path.join(extensionsPath, '**', 'esbuild.browser.mts'),
|
||||
{ ignore: ['**/node_modules'] }
|
||||
);
|
||||
|
||||
@@ -293,7 +293,11 @@ async function buildWebExtensions(isWatch: boolean): Promise<void> {
|
||||
|
||||
// Esbuild for extensions
|
||||
if (esbuildConfigLocations.length > 0) {
|
||||
promises.push(ext.esbuildExtensions('packaging web extension (esbuild)', isWatch, esbuildConfigLocations.map(script => ({ script }))));
|
||||
promises.push(
|
||||
ext.esbuildExtensions('packaging web extension (esbuild)', isWatch, esbuildConfigLocations.map(script => ({ script }))),
|
||||
// Also run type check on extensions
|
||||
...esbuildConfigLocations.map(script => ext.typeCheckExtension(path.dirname(script), true))
|
||||
);
|
||||
}
|
||||
|
||||
// Run webpack for remaining extensions
|
||||
|
||||
@@ -25,6 +25,7 @@ import { getProductionDependencies } from './dependencies.ts';
|
||||
import { type IExtensionDefinition, getExtensionStream } from './builtInExtensions.ts';
|
||||
import { getVersion } from './getVersion.ts';
|
||||
import { fetchUrls, fetchGithub } from './fetch.ts';
|
||||
import { createTsgoStream, spawnTsgo } from './tsgo.ts';
|
||||
import vzip from 'gulp-vinyl-zip';
|
||||
|
||||
import { createRequire } from 'module';
|
||||
@@ -67,23 +68,27 @@ function updateExtensionPackageJSON(input: Stream, update: (data: any) => any):
|
||||
function fromLocal(extensionPath: string, forWeb: boolean, disableMangle: boolean): Stream {
|
||||
|
||||
const esbuildConfigFileName = forWeb
|
||||
? 'esbuild-browser.ts'
|
||||
: 'esbuild.ts';
|
||||
? 'esbuild.browser.mts'
|
||||
: 'esbuild.mts';
|
||||
|
||||
const webpackConfigFileName = forWeb
|
||||
? `extension-browser.webpack.config.js`
|
||||
: `extension.webpack.config.js`;
|
||||
|
||||
const hasEsbuild = fs.existsSync(path.join(extensionPath, esbuildConfigFileName));
|
||||
const isWebPacked = fs.existsSync(path.join(extensionPath, webpackConfigFileName));
|
||||
const hasWebpack = fs.existsSync(path.join(extensionPath, webpackConfigFileName));
|
||||
|
||||
let input: Stream;
|
||||
let isBundled = false;
|
||||
|
||||
if (hasEsbuild) {
|
||||
input = fromLocalEsbuild(extensionPath, esbuildConfigFileName);
|
||||
// Unlike webpack, esbuild only does bundling so we still want to run a separate type check step
|
||||
input = es.merge(
|
||||
fromLocalEsbuild(extensionPath, esbuildConfigFileName),
|
||||
typeCheckExtensionStream(extensionPath, forWeb),
|
||||
);
|
||||
isBundled = true;
|
||||
} else if (isWebPacked) {
|
||||
} else if (hasWebpack) {
|
||||
input = fromLocalWebpack(extensionPath, webpackConfigFileName, disableMangle);
|
||||
isBundled = true;
|
||||
} else {
|
||||
@@ -105,6 +110,17 @@ function fromLocal(extensionPath: string, forWeb: boolean, disableMangle: boolea
|
||||
return input;
|
||||
}
|
||||
|
||||
export function typeCheckExtension(extensionPath: string, forWeb: boolean): Promise<void> {
|
||||
const tsconfigFileName = forWeb ? 'tsconfig.browser.json' : 'tsconfig.json';
|
||||
const tsconfigPath = path.join(extensionPath, tsconfigFileName);
|
||||
return spawnTsgo(tsconfigPath, { taskName: 'typechecking extension (tsgo)', noEmit: true });
|
||||
}
|
||||
|
||||
export function typeCheckExtensionStream(extensionPath: string, forWeb: boolean): Stream {
|
||||
const tsconfigFileName = forWeb ? 'tsconfig.browser.json' : 'tsconfig.json';
|
||||
const tsconfigPath = path.join(extensionPath, tsconfigFileName);
|
||||
return createTsgoStream(tsconfigPath, { taskName: 'typechecking extension (tsgo)', noEmit: true });
|
||||
}
|
||||
|
||||
function fromLocalWebpack(extensionPath: string, webpackConfigFileName: string, disableMangle: boolean): Stream {
|
||||
const vsce = require('@vscode/vsce') as typeof import('@vscode/vsce');
|
||||
@@ -267,6 +283,7 @@ function fromLocalEsbuild(extensionPath: string, esbuildConfigFileName: string):
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
const matches = (stderr || '').match(/\> (.+): error: (.+)?/g);
|
||||
fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), esbuildConfigFileName))} with ${matches ? matches.length : 0} errors.`);
|
||||
for (const match of matches || []) {
|
||||
@@ -632,17 +649,6 @@ export function translatePackageJSON(packageJSON: string, packageNLSPath: string
|
||||
|
||||
const extensionsPath = path.join(root, 'extensions');
|
||||
|
||||
// Additional projects to run esbuild on. These typically build code for webviews
|
||||
const esbuildMediaScripts = [
|
||||
'ipynb/esbuild.mjs',
|
||||
'markdown-language-features/esbuild-notebook.mjs',
|
||||
'markdown-language-features/esbuild-preview.mjs',
|
||||
'markdown-math/esbuild.mjs',
|
||||
'mermaid-chat-features/esbuild-chat-webview.mjs',
|
||||
'notebook-renderers/esbuild.mjs',
|
||||
'simple-browser/esbuild-preview.mjs',
|
||||
];
|
||||
|
||||
export async function webpackExtensions(taskName: string, isWatch: boolean, webpackConfigLocations: { configPath: string; outputRoot?: string }[]) {
|
||||
const webpack = require('webpack') as typeof import('webpack');
|
||||
|
||||
@@ -742,6 +748,18 @@ export async function esbuildExtensions(taskName: string, isWatch: boolean, scri
|
||||
await Promise.all(tasks);
|
||||
}
|
||||
|
||||
|
||||
// Additional projects to run esbuild on. These typically build code for webviews
|
||||
const esbuildMediaScripts = [
|
||||
'ipynb/esbuild.notebook.mts',
|
||||
'markdown-language-features/esbuild.notebook.mts',
|
||||
'markdown-language-features/esbuild.webview.mts',
|
||||
'markdown-math/esbuild.notebook.mts',
|
||||
'mermaid-chat-features/esbuild.webview.mts',
|
||||
'notebook-renderers/esbuild.notebook.mts',
|
||||
'simple-browser/esbuild.webview.mts',
|
||||
];
|
||||
|
||||
export function buildExtensionMedia(isWatch: boolean, outputRoot?: string): Promise<void> {
|
||||
return esbuildExtensions('esbuilding extension media', isWatch, esbuildMediaScripts.map(p => ({
|
||||
script: path.join(extensionsPath, p),
|
||||
|
||||
@@ -3,37 +3,31 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import ansiColors from 'ansi-colors';
|
||||
import * as cp from 'child_process';
|
||||
import es from 'event-stream';
|
||||
import fancyLog from 'fancy-log';
|
||||
import * as path from 'path';
|
||||
import { createReporter } from './reporter.ts';
|
||||
|
||||
const root = path.dirname(path.dirname(import.meta.dirname));
|
||||
const npx = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
||||
const ansiRegex = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
|
||||
|
||||
export function spawnTsgo(projectPath: string, config: { reporterId: string }, onComplete?: () => Promise<void> | void): Promise<void> {
|
||||
const reporter = createReporter(config.reporterId);
|
||||
let report: NodeJS.ReadWriteStream | undefined;
|
||||
|
||||
const beginReport = (emitError: boolean) => {
|
||||
if (report) {
|
||||
report.end();
|
||||
export function spawnTsgo(projectPath: string, config: { taskName: string; noEmit?: boolean }, onComplete?: () => Promise<void> | void): Promise<void> {
|
||||
function reporter(stdError: string) {
|
||||
const matches = (stdError || '').match(/^error \w+: (.+)?/g);
|
||||
fancyLog(`Finished ${ansiColors.green(config.taskName)} ${projectPath} with ${matches ? matches.length : 0} errors.`);
|
||||
for (const match of matches || []) {
|
||||
fancyLog.error(match);
|
||||
}
|
||||
report = reporter.end(emitError);
|
||||
};
|
||||
}
|
||||
|
||||
const endReport = () => {
|
||||
if (!report) {
|
||||
return;
|
||||
}
|
||||
report.end();
|
||||
report = undefined;
|
||||
};
|
||||
|
||||
beginReport(false);
|
||||
|
||||
const args = ['tsgo', '--project', projectPath, '--pretty', 'false', '--sourceMap', '--inlineSources'];
|
||||
const args = ['tsgo', '--project', projectPath, '--pretty', 'false'];
|
||||
if (config.noEmit) {
|
||||
args.push('--noEmit');
|
||||
} else {
|
||||
args.push('--sourceMap', '--inlineSources');
|
||||
}
|
||||
const child = cp.spawn(npx, args, {
|
||||
cwd: root,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
@@ -47,23 +41,13 @@ export function spawnTsgo(projectPath: string, config: { reporterId: string }, o
|
||||
return;
|
||||
}
|
||||
if (/Starting compilation|File change detected/i.test(trimmed)) {
|
||||
beginReport(false);
|
||||
return;
|
||||
}
|
||||
if (/Compilation complete/i.test(trimmed)) {
|
||||
endReport();
|
||||
return;
|
||||
}
|
||||
|
||||
const match = /(.*\(\d+,\d+\): )(.*: )(.*)/.exec(trimmed);
|
||||
|
||||
if (match) {
|
||||
const fullpath = path.isAbsolute(match[1]) ? match[1] : path.join(root, match[1]);
|
||||
const message = match[3];
|
||||
reporter(fullpath + message);
|
||||
} else {
|
||||
reporter(trimmed);
|
||||
}
|
||||
reporter(trimmed);
|
||||
};
|
||||
|
||||
const handleData = (data: Buffer) => {
|
||||
@@ -84,7 +68,7 @@ export function spawnTsgo(projectPath: string, config: { reporterId: string }, o
|
||||
handleLine(buffer);
|
||||
buffer = '';
|
||||
}
|
||||
endReport();
|
||||
|
||||
if (code === 0) {
|
||||
Promise.resolve(onComplete?.()).then(() => resolve(), reject);
|
||||
} else {
|
||||
@@ -93,15 +77,13 @@ export function spawnTsgo(projectPath: string, config: { reporterId: string }, o
|
||||
});
|
||||
|
||||
child.on('error', err => {
|
||||
endReport();
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function createTsgoStream(projectPath: string, config: { reporterId: string }, onComplete?: () => Promise<void> | void): NodeJS.ReadWriteStream {
|
||||
export function createTsgoStream(projectPath: string, config: { taskName: string; noEmit?: boolean }, onComplete?: () => Promise<void> | void): NodeJS.ReadWriteStream {
|
||||
const stream = es.through();
|
||||
|
||||
spawnTsgo(projectPath, config, onComplete).then(() => {
|
||||
stream.emit('end');
|
||||
}).catch(() => {
|
||||
|
||||
@@ -2,27 +2,22 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// @ts-check
|
||||
|
||||
/**
|
||||
* @fileoverview Common build script for extension scripts used in in webviews.
|
||||
* Common build script for extension scripts used in in webviews.
|
||||
*/
|
||||
import path from 'node:path';
|
||||
import esbuild from 'esbuild';
|
||||
|
||||
/**
|
||||
* @typedef {Partial<import('esbuild').BuildOptions> & {
|
||||
* entryPoints: string[] | Record<string, string> | { in: string, out: string }[];
|
||||
* outdir: string;
|
||||
* }} BuildOptions
|
||||
*/
|
||||
export type BuildOptions = Partial<esbuild.BuildOptions> & {
|
||||
entryPoints: string[] | Record<string, string> | { in: string; out: string }[];
|
||||
outdir: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the source code once using esbuild.
|
||||
*
|
||||
* @param {BuildOptions} options
|
||||
* @param {(outDir: string) => unknown} [didBuild]
|
||||
*/
|
||||
async function build(options, didBuild) {
|
||||
async function build(options: BuildOptions, didBuild?: (outDir: string) => unknown): Promise<void> {
|
||||
await esbuild.build({
|
||||
bundle: true,
|
||||
minify: true,
|
||||
@@ -38,11 +33,8 @@ async function build(options, didBuild) {
|
||||
|
||||
/**
|
||||
* Build the source code once using esbuild, logging errors instead of throwing.
|
||||
*
|
||||
* @param {BuildOptions} options
|
||||
* @param {(outDir: string) => unknown} [didBuild]
|
||||
*/
|
||||
async function tryBuild(options, didBuild) {
|
||||
async function tryBuild(options: BuildOptions, didBuild?: (outDir: string) => unknown): Promise<void> {
|
||||
try {
|
||||
await build(options, didBuild);
|
||||
} catch (err) {
|
||||
@@ -50,17 +42,16 @@ async function tryBuild(options, didBuild) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* srcDir: string;
|
||||
* outdir: string;
|
||||
* entryPoints: string[] | Record<string, string> | { in: string, out: string }[];
|
||||
* additionalOptions?: Partial<import('esbuild').BuildOptions>
|
||||
* }} config
|
||||
* @param {string[]} args
|
||||
* @param {(outDir: string) => unknown} [didBuild]
|
||||
*/
|
||||
export async function run(config, args, didBuild) {
|
||||
export async function run(
|
||||
config: {
|
||||
srcDir: string;
|
||||
outdir: string;
|
||||
entryPoints: BuildOptions['entryPoints'];
|
||||
additionalOptions?: Partial<esbuild.BuildOptions>;
|
||||
},
|
||||
args: string[],
|
||||
didBuild?: (outDir: string) => unknown
|
||||
): Promise<void> {
|
||||
let outdir = config.outdir;
|
||||
const outputRootIndex = args.indexOf('--outputRoot');
|
||||
if (outputRootIndex >= 0) {
|
||||
@@ -69,8 +60,7 @@ export async function run(config, args, didBuild) {
|
||||
outdir = path.join(outputRoot, outputDirName);
|
||||
}
|
||||
|
||||
/** @type {BuildOptions} */
|
||||
const resolvedOptions = {
|
||||
const resolvedOptions: BuildOptions = {
|
||||
entryPoints: config.entryPoints,
|
||||
outdir,
|
||||
logOverride: {
|
||||
@@ -2,9 +2,8 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// @ts-check
|
||||
import path from 'node:path';
|
||||
import { run } from '../esbuild-webview-common.mjs';
|
||||
import { run } from '../esbuild-webview-common.mts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'notebook-src');
|
||||
const outDir = path.join(import.meta.dirname, 'notebook-out');
|
||||
@@ -158,7 +158,7 @@
|
||||
"scripts": {
|
||||
"compile": "npx gulp compile-extension:ipynb && npm run build-notebook",
|
||||
"watch": "npx gulp watch-extension:ipynb",
|
||||
"build-notebook": "node ./esbuild.mjs"
|
||||
"build-notebook": "node ./esbuild.notebook.mts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@enonic/fnv-plus": "^1.3.0",
|
||||
|
||||
@@ -2,16 +2,12 @@ test/**
|
||||
test-workspace/**
|
||||
src/**
|
||||
notebook/**
|
||||
tsconfig.json
|
||||
tsconfig.*.json
|
||||
tsconfig*.json
|
||||
esbuild*
|
||||
out/test/**
|
||||
out/**
|
||||
extension.webpack.config.js
|
||||
extension-browser.webpack.config.js
|
||||
cgmanifest.json
|
||||
package-lock.json
|
||||
preview-src/**
|
||||
webpack.config.js
|
||||
esbuild-*
|
||||
.gitignore
|
||||
**/*.d.ts
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
import { run } from '../esbuild-extension-common.mts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist', 'browser');
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
import { run } from '../esbuild-extension-common.mts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist');
|
||||
@@ -2,9 +2,8 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// @ts-check
|
||||
import path from 'path';
|
||||
import { run } from '../esbuild-webview-common.mjs';
|
||||
import { run } from '../esbuild-webview-common.mts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'notebook');
|
||||
const outDir = path.join(import.meta.dirname, 'notebook-out');
|
||||
@@ -2,9 +2,8 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// @ts-check
|
||||
import path from 'path';
|
||||
import { run } from '../esbuild-webview-common.mjs';
|
||||
import { run } from '../esbuild-webview-common.mts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'preview-src');
|
||||
const outDir = path.join(import.meta.dirname, 'media');
|
||||
@@ -757,14 +757,20 @@
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "gulp compile-extension:markdown-language-features-languageService && gulp compile-extension:markdown-language-features && npm run build-preview && npm run build-notebook",
|
||||
"watch": "npm run build-preview && gulp watch-extension:markdown-language-features watch-extension:markdown-language-features-languageService",
|
||||
"vscode:prepublish": "npm run build-ext && npm run build-preview",
|
||||
"build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.mjs compile-extension:markdown-language-features ./tsconfig.json",
|
||||
"build-notebook": "node ./esbuild-notebook.mjs",
|
||||
"build-preview": "node ./esbuild-preview.mjs",
|
||||
"compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none",
|
||||
"watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch"
|
||||
"compile": "npm-run-all2 -lp build-ext build-webview build-notebook",
|
||||
"watch": "npm-run-all2 -lp watch-ext watch-webview watch-notebook",
|
||||
"build-ext": "gulp compile-extension:markdown-language-features",
|
||||
"watch-ext": "gulp watch-extension:markdown-language-features",
|
||||
"build-notebook": "node ./esbuild.notebook.mts",
|
||||
"watch-notebook": "node ./esbuild.notebook.mts --watch",
|
||||
"build-webview": "node ./esbuild.webview.mts",
|
||||
"watch-webview": "node ./esbuild.webview.mts --watch",
|
||||
"compile-web": "npm-run-all2 -lp bundle-web typecheck-web",
|
||||
"bundle-web": "node ./esbuild.browser.mts",
|
||||
"typecheck-web": "tsgo --project ./tsconfig.browser.json --noEmit",
|
||||
"watch-web": "npm-run-all2 -lp watch-bundle-web watch-typecheck-web",
|
||||
"watch-bundle-web": "node ./esbuild.browser.mts --watch",
|
||||
"watch-typecheck-web": "tsgo --project ./tsconfig.browser.json --noEmit --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vscode/extension-telemetry": "^0.9.8",
|
||||
|
||||
@@ -3,5 +3,8 @@
|
||||
"compilerOptions": {},
|
||||
"exclude": [
|
||||
"./src/test/**"
|
||||
],
|
||||
"files": [
|
||||
"./src/extension.browser.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
src/**
|
||||
notebook/**
|
||||
extension-browser.webpack.config.js
|
||||
extension.webpack.config.js
|
||||
esbuild.*
|
||||
tsconfig*.json
|
||||
esbuild*
|
||||
cgmanifest.json
|
||||
package-lock.json
|
||||
webpack.config.js
|
||||
tsconfig.json
|
||||
.gitignore
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as path from 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
import { run } from '../esbuild-extension-common.mts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist', 'browser');
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as path from 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
import { run } from '../esbuild-extension-common.mts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist');
|
||||
@@ -2,18 +2,14 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
//@ts-check
|
||||
|
||||
import path from 'path';
|
||||
import fse from 'fs-extra';
|
||||
import { run } from '../esbuild-webview-common.mjs';
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
import path from 'path';
|
||||
import { run } from '../esbuild-webview-common.mts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'notebook');
|
||||
const outDir = path.join(import.meta.dirname, 'notebook-out');
|
||||
|
||||
function postBuild(outDir) {
|
||||
function postBuild(outDir: string) {
|
||||
fse.copySync(
|
||||
path.join(import.meta.dirname, 'node_modules', 'katex', 'dist', 'katex.min.css'),
|
||||
path.join(outDir, 'katex.min.css'));
|
||||
@@ -108,7 +108,7 @@
|
||||
"scripts": {
|
||||
"compile": "npm run build-notebook",
|
||||
"watch": "npm run build-notebook",
|
||||
"build-notebook": "node ./esbuild.mjs"
|
||||
"build-notebook": "node ./esbuild.notebook.mts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/markdown-it": "^0.0.0",
|
||||
|
||||
9
extensions/markdown-math/tsconfig.browser.json
Normal file
9
extensions/markdown-math/tsconfig.browser.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": [],
|
||||
"typeRoots": [
|
||||
"./node_modules/@types"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
test/**
|
||||
src/**
|
||||
tsconfig.json
|
||||
tsconfig*.json
|
||||
esbuild*
|
||||
out/test/**
|
||||
out/**
|
||||
extension.webpack.config.js
|
||||
extension-browser.webpack.config.js
|
||||
cgmanifest.json
|
||||
package-lock.json
|
||||
preview-src/**
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as path from 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
import { run } from '../esbuild-extension-common.mts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist', 'browser');
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as path from 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
import { run } from '../esbuild-extension-common.mts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist');
|
||||
3
extensions/media-preview/tsconfig.browser.json
Normal file
3
extensions/media-preview/tsconfig.browser.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "./tsconfig"
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as path from 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
import { run } from '../esbuild-extension-common.mts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist', 'browser');
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as path from 'node:path';
|
||||
import { run } from '../esbuild-extension-common.ts';
|
||||
import { run } from '../esbuild-extension-common.mts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'dist');
|
||||
@@ -2,9 +2,8 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// @ts-check
|
||||
import path from 'path';
|
||||
import { run } from '../esbuild-webview-common.mjs';
|
||||
import { run } from '../esbuild-webview-common.mts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'chat-webview-src');
|
||||
const outDir = path.join(import.meta.dirname, 'chat-webview-out');
|
||||
@@ -117,7 +117,7 @@
|
||||
"watch": "npm run build-chat-webview && gulp watch-extension:mermaid-chat-features",
|
||||
"vscode:prepublish": "npm run build-ext && npm run build-chat-webview",
|
||||
"build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.mjs compile-extension:mermaid-chat-features",
|
||||
"build-chat-webview": "node ./esbuild-chat-webview.mjs",
|
||||
"build-chat-webview": "node ./esbuild.webview.mts",
|
||||
"compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none",
|
||||
"watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose"
|
||||
},
|
||||
|
||||
3
extensions/mermaid-chat-features/tsconfig.browser.json
Normal file
3
extensions/mermaid-chat-features/tsconfig.browser.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "./tsconfig"
|
||||
}
|
||||
@@ -2,9 +2,8 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// @ts-check
|
||||
import path from 'path';
|
||||
import { run } from '../esbuild-webview-common.mjs';
|
||||
import { run } from '../esbuild-webview-common.mts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'src');
|
||||
const outDir = path.join(import.meta.dirname, 'renderer-out');
|
||||
@@ -44,7 +44,7 @@
|
||||
"scripts": {
|
||||
"compile": "npx gulp compile-extension:notebook-renderers && npm run build-notebook",
|
||||
"watch": "npx gulp compile-watch:notebook-renderers",
|
||||
"build-notebook": "node ./esbuild.mjs"
|
||||
"build-notebook": "node ./esbuild.notebook.mts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jsdom": "^21.1.0",
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// @ts-check
|
||||
import path from 'path';
|
||||
import { run } from '../esbuild-webview-common.mjs';
|
||||
import { run } from '../esbuild-webview-common.mts';
|
||||
|
||||
const srcDir = path.join(import.meta.dirname, 'preview-src');
|
||||
const outDir = path.join(import.meta.dirname, 'media');
|
||||
@@ -67,11 +67,11 @@
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "gulp compile-extension:simple-browser && npm run build-preview",
|
||||
"watch": "npm run build-preview && gulp watch-extension:simple-browser",
|
||||
"vscode:prepublish": "npm run build-ext && npm run build-preview",
|
||||
"compile": "gulp compile-extension:simple-browser && npm run build-webview",
|
||||
"watch": "npm run build-webview && gulp watch-extension:simple-browser",
|
||||
"vscode:prepublish": "npm run build-ext && npm run build-webview",
|
||||
"build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.mjs compile-extension:simple-browser ./tsconfig.json",
|
||||
"build-preview": "node ./esbuild-preview.mjs",
|
||||
"build-webview": "node ./esbuild.webview.mts",
|
||||
"compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none",
|
||||
"watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose"
|
||||
},
|
||||
|
||||
@@ -1296,7 +1296,7 @@
|
||||
"title": "%configuration.inlayHints%",
|
||||
"order": 24,
|
||||
"properties": {
|
||||
"typescript.inlayHints.parameterNames.enabled": {
|
||||
"js/ts.inlayHints.parameterNames.enabled": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"none",
|
||||
@@ -1310,49 +1310,11 @@
|
||||
],
|
||||
"default": "none",
|
||||
"markdownDescription": "%configuration.inlayHints.parameterNames.enabled%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.parameterNames.suppressWhenArgumentMatchesName": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "%configuration.inlayHints.parameterNames.suppressWhenArgumentMatchesName%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.parameterTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.parameterTypes.enabled%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.variableTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.variableTypes.enabled%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.variableTypes.suppressWhenTypeMatchesName": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "%configuration.inlayHints.variableTypes.suppressWhenTypeMatchesName%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.propertyDeclarationTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.propertyDeclarationTypes.enabled%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.functionLikeReturnTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.functionLikeReturnTypes.enabled%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.enumMemberValues.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.enumMemberValues.enabled%",
|
||||
"scope": "resource"
|
||||
"scope": "language-overridable",
|
||||
"tags": [
|
||||
"JavaScript",
|
||||
"TypeScript"
|
||||
]
|
||||
},
|
||||
"javascript.inlayHints.parameterNames.enabled": {
|
||||
"type": "string",
|
||||
@@ -1368,42 +1330,184 @@
|
||||
],
|
||||
"default": "none",
|
||||
"markdownDescription": "%configuration.inlayHints.parameterNames.enabled%",
|
||||
"markdownDeprecationMessage": "%configuration.inlayHints.parameterNames.enabled.unifiedDeprecationMessage%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.parameterNames.enabled": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"none",
|
||||
"literals",
|
||||
"all"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"%inlayHints.parameterNames.none%",
|
||||
"%inlayHints.parameterNames.literals%",
|
||||
"%inlayHints.parameterNames.all%"
|
||||
],
|
||||
"default": "none",
|
||||
"markdownDescription": "%configuration.inlayHints.parameterNames.enabled%",
|
||||
"markdownDeprecationMessage": "%configuration.inlayHints.parameterNames.enabled.unifiedDeprecationMessage%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"js/ts.inlayHints.parameterNames.suppressWhenArgumentMatchesName": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "%configuration.inlayHints.parameterNames.suppressWhenArgumentMatchesName%",
|
||||
"scope": "language-overridable",
|
||||
"tags": [
|
||||
"JavaScript",
|
||||
"TypeScript"
|
||||
]
|
||||
},
|
||||
"javascript.inlayHints.parameterNames.suppressWhenArgumentMatchesName": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "%configuration.inlayHints.parameterNames.suppressWhenArgumentMatchesName%",
|
||||
"markdownDeprecationMessage": "%configuration.inlayHints.parameterNames.suppressWhenArgumentMatchesName.unifiedDeprecationMessage%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.parameterNames.suppressWhenArgumentMatchesName": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "%configuration.inlayHints.parameterNames.suppressWhenArgumentMatchesName%",
|
||||
"markdownDeprecationMessage": "%configuration.inlayHints.parameterNames.suppressWhenArgumentMatchesName.unifiedDeprecationMessage%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"js/ts.inlayHints.parameterTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.parameterTypes.enabled%",
|
||||
"scope": "language-overridable",
|
||||
"tags": [
|
||||
"JavaScript",
|
||||
"TypeScript"
|
||||
]
|
||||
},
|
||||
"javascript.inlayHints.parameterTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.parameterTypes.enabled%",
|
||||
"markdownDeprecationMessage": "%configuration.inlayHints.parameterTypes.enabled.unifiedDeprecationMessage%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.parameterTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.parameterTypes.enabled%",
|
||||
"markdownDeprecationMessage": "%configuration.inlayHints.parameterTypes.enabled.unifiedDeprecationMessage%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"js/ts.inlayHints.variableTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.variableTypes.enabled%",
|
||||
"scope": "language-overridable",
|
||||
"tags": [
|
||||
"JavaScript",
|
||||
"TypeScript"
|
||||
]
|
||||
},
|
||||
"javascript.inlayHints.variableTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.variableTypes.enabled%",
|
||||
"markdownDeprecationMessage": "%configuration.inlayHints.variableTypes.enabled.unifiedDeprecationMessage%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.variableTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.variableTypes.enabled%",
|
||||
"markdownDeprecationMessage": "%configuration.inlayHints.variableTypes.enabled.unifiedDeprecationMessage%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"js/ts.inlayHints.variableTypes.suppressWhenTypeMatchesName": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "%configuration.inlayHints.variableTypes.suppressWhenTypeMatchesName%",
|
||||
"scope": "language-overridable",
|
||||
"tags": [
|
||||
"JavaScript",
|
||||
"TypeScript"
|
||||
]
|
||||
},
|
||||
"javascript.inlayHints.variableTypes.suppressWhenTypeMatchesName": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "%configuration.inlayHints.variableTypes.suppressWhenTypeMatchesName%",
|
||||
"markdownDeprecationMessage": "%configuration.inlayHints.variableTypes.suppressWhenTypeMatchesName.unifiedDeprecationMessage%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.variableTypes.suppressWhenTypeMatchesName": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "%configuration.inlayHints.variableTypes.suppressWhenTypeMatchesName%",
|
||||
"markdownDeprecationMessage": "%configuration.inlayHints.variableTypes.suppressWhenTypeMatchesName.unifiedDeprecationMessage%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"js/ts.inlayHints.propertyDeclarationTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.propertyDeclarationTypes.enabled%",
|
||||
"scope": "language-overridable",
|
||||
"tags": [
|
||||
"JavaScript",
|
||||
"TypeScript"
|
||||
]
|
||||
},
|
||||
"javascript.inlayHints.propertyDeclarationTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.propertyDeclarationTypes.enabled%",
|
||||
"markdownDeprecationMessage": "%configuration.inlayHints.propertyDeclarationTypes.enabled.unifiedDeprecationMessage%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.propertyDeclarationTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.propertyDeclarationTypes.enabled%",
|
||||
"markdownDeprecationMessage": "%configuration.inlayHints.propertyDeclarationTypes.enabled.unifiedDeprecationMessage%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"js/ts.inlayHints.functionLikeReturnTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.functionLikeReturnTypes.enabled%",
|
||||
"scope": "language-overridable",
|
||||
"tags": [
|
||||
"JavaScript",
|
||||
"TypeScript"
|
||||
]
|
||||
},
|
||||
"javascript.inlayHints.functionLikeReturnTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.functionLikeReturnTypes.enabled%",
|
||||
"markdownDeprecationMessage": "%configuration.inlayHints.functionLikeReturnTypes.enabled.unifiedDeprecationMessage%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"typescript.inlayHints.functionLikeReturnTypes.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.functionLikeReturnTypes.enabled%",
|
||||
"markdownDeprecationMessage": "%configuration.inlayHints.functionLikeReturnTypes.enabled.unifiedDeprecationMessage%",
|
||||
"scope": "resource"
|
||||
},
|
||||
"js/ts.inlayHints.enumMemberValues.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.enumMemberValues.enabled%",
|
||||
"scope": "language-overridable",
|
||||
"tags": [
|
||||
"TypeScript"
|
||||
]
|
||||
},
|
||||
"typescript.inlayHints.enumMemberValues.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "%configuration.inlayHints.enumMemberValues.enabled%",
|
||||
"markdownDeprecationMessage": "%configuration.inlayHints.enumMemberValues.enabled.unifiedDeprecationMessage%",
|
||||
"scope": "resource"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,38 +103,46 @@
|
||||
"The text inside the ``` block is code and should not be localized."
|
||||
]
|
||||
},
|
||||
"configuration.inlayHints.parameterNames.enabled.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.inlayHints.parameterNames.enabled#` instead.",
|
||||
"configuration.inlayHints.parameterNames.suppressWhenArgumentMatchesName": "Suppress parameter name hints on arguments whose text is identical to the parameter name.",
|
||||
"configuration.inlayHints.parameterNames.suppressWhenArgumentMatchesName.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.inlayHints.parameterNames.suppressWhenArgumentMatchesName#` instead.",
|
||||
"configuration.inlayHints.parameterTypes.enabled": {
|
||||
"message": "Enable/disable inlay hints for implicit parameter types:\n```typescript\n\nel.addEventListener('click', e /* :MouseEvent */ => ...)\n \n```",
|
||||
"comment": [
|
||||
"The text inside the ``` block is code and should not be localized."
|
||||
]
|
||||
},
|
||||
"configuration.inlayHints.parameterTypes.enabled.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.inlayHints.parameterTypes.enabled#` instead.",
|
||||
"configuration.inlayHints.variableTypes.enabled": {
|
||||
"message": "Enable/disable inlay hints for implicit variable types:\n```typescript\n\nconst foo /* :number */ = Date.now();\n \n```",
|
||||
"comment": [
|
||||
"The text inside the ``` block is code and should not be localized."
|
||||
]
|
||||
},
|
||||
"configuration.inlayHints.variableTypes.enabled.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.inlayHints.variableTypes.enabled#` instead.",
|
||||
"configuration.inlayHints.variableTypes.suppressWhenTypeMatchesName": "Suppress type hints on variables whose name is identical to the type name.",
|
||||
"configuration.inlayHints.variableTypes.suppressWhenTypeMatchesName.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.inlayHints.variableTypes.suppressWhenTypeMatchesName#` instead.",
|
||||
"configuration.inlayHints.propertyDeclarationTypes.enabled": {
|
||||
"message": "Enable/disable inlay hints for implicit types on property declarations:\n```typescript\n\nclass Foo {\n\tprop /* :number */ = Date.now();\n}\n \n```",
|
||||
"comment": [
|
||||
"The text inside the ``` block is code and should not be localized."
|
||||
]
|
||||
},
|
||||
"configuration.inlayHints.propertyDeclarationTypes.enabled.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.inlayHints.propertyDeclarationTypes.enabled#` instead.",
|
||||
"configuration.inlayHints.functionLikeReturnTypes.enabled": {
|
||||
"message": "Enable/disable inlay hints for implicit return types on function signatures:\n```typescript\n\nfunction foo() /* :number */ {\n\treturn Date.now();\n} \n \n```",
|
||||
"comment": [
|
||||
"The text inside the ``` block is code and should not be localized."
|
||||
]
|
||||
},
|
||||
"configuration.inlayHints.functionLikeReturnTypes.enabled.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.inlayHints.functionLikeReturnTypes.enabled#` instead.",
|
||||
"configuration.inlayHints.enumMemberValues.enabled": {
|
||||
"message": "Enable/disable inlay hints for member values in enum declarations:\n```typescript\n\nenum MyValue {\n\tA /* = 0 */;\n\tB /* = 1 */;\n}\n \n```",
|
||||
"comment": [
|
||||
"The text inside the ``` block is code and should not be localized."
|
||||
]
|
||||
},
|
||||
"configuration.inlayHints.enumMemberValues.enabled.unifiedDeprecationMessage": "This setting is deprecated. Use `#js/ts.inlayHints.enumMemberValues.enabled#` instead.",
|
||||
"taskDefinition.tsconfig.description": "The tsconfig file that defines the TS build.",
|
||||
"javascript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for JavaScript files in the editor.",
|
||||
"typescript.suggestionActions.enabled": "Enable/disable suggestion diagnostics for TypeScript files in the editor.",
|
||||
|
||||
@@ -10,6 +10,7 @@ import { isTypeScriptDocument } from '../configuration/languageIds';
|
||||
import { API } from '../tsServer/api';
|
||||
import type * as Proto from '../tsServer/protocol/protocol';
|
||||
import { ITypeScriptServiceClient } from '../typescriptService';
|
||||
import { readUnifiedConfig } from '../utils/configuration';
|
||||
import { Disposable } from '../utils/dispose';
|
||||
import { equals } from '../utils/objects';
|
||||
import { ResourceMap } from '../utils/resourceMap';
|
||||
@@ -206,7 +207,7 @@ export default class FileConfigurationManager extends Disposable {
|
||||
disableLineTextInReferences: true,
|
||||
interactiveInlayHints: true,
|
||||
includeCompletionsForModuleExports: config.get<boolean>('suggest.autoImports'),
|
||||
...getInlayHintsPreferences(config),
|
||||
...getInlayHintsPreferences(document, isTypeScriptDocument(document) ? 'typescript' : 'javascript'),
|
||||
...this.getOrganizeImportsPreferences(preferencesConfig),
|
||||
maximumHoverLength: this.getMaximumHoverLength(document),
|
||||
};
|
||||
@@ -274,31 +275,32 @@ function withDefaultAsUndefined<T, O extends T>(value: T, def: O): Exclude<T, O>
|
||||
return value === def ? undefined : value as Exclude<T, O>;
|
||||
}
|
||||
|
||||
export class InlayHintSettingNames {
|
||||
static readonly parameterNamesSuppressWhenArgumentMatchesName = 'inlayHints.parameterNames.suppressWhenArgumentMatchesName';
|
||||
static readonly parameterNamesEnabled = 'inlayHints.parameterTypes.enabled';
|
||||
static readonly variableTypesEnabled = 'inlayHints.variableTypes.enabled';
|
||||
static readonly variableTypesSuppressWhenTypeMatchesName = 'inlayHints.variableTypes.suppressWhenTypeMatchesName';
|
||||
static readonly propertyDeclarationTypesEnabled = 'inlayHints.propertyDeclarationTypes.enabled';
|
||||
static readonly functionLikeReturnTypesEnabled = 'inlayHints.functionLikeReturnTypes.enabled';
|
||||
static readonly enumMemberValuesEnabled = 'inlayHints.enumMemberValues.enabled';
|
||||
}
|
||||
export const InlayHintSettingNames = Object.freeze({
|
||||
parameterNamesEnabled: 'inlayHints.parameterNames.enabled',
|
||||
parameterNamesSuppressWhenArgumentMatchesName: 'inlayHints.parameterNames.suppressWhenArgumentMatchesName',
|
||||
parameterTypesEnabled: 'inlayHints.parameterTypes.enabled',
|
||||
variableTypesEnabled: 'inlayHints.variableTypes.enabled',
|
||||
variableTypesSuppressWhenTypeMatchesName: 'inlayHints.variableTypes.suppressWhenTypeMatchesName',
|
||||
propertyDeclarationTypesEnabled: 'inlayHints.propertyDeclarationTypes.enabled',
|
||||
functionLikeReturnTypesEnabled: 'inlayHints.functionLikeReturnTypes.enabled',
|
||||
enumMemberValuesEnabled: 'inlayHints.enumMemberValues.enabled',
|
||||
});
|
||||
|
||||
export function getInlayHintsPreferences(config: vscode.WorkspaceConfiguration) {
|
||||
export function getInlayHintsPreferences(scope: vscode.ConfigurationScope, fallbackSection: string) {
|
||||
return {
|
||||
includeInlayParameterNameHints: getInlayParameterNameHintsPreference(config),
|
||||
includeInlayParameterNameHintsWhenArgumentMatchesName: !config.get<boolean>(InlayHintSettingNames.parameterNamesSuppressWhenArgumentMatchesName, true),
|
||||
includeInlayFunctionParameterTypeHints: config.get<boolean>(InlayHintSettingNames.parameterNamesEnabled, false),
|
||||
includeInlayVariableTypeHints: config.get<boolean>(InlayHintSettingNames.variableTypesEnabled, false),
|
||||
includeInlayVariableTypeHintsWhenTypeMatchesName: !config.get<boolean>(InlayHintSettingNames.variableTypesSuppressWhenTypeMatchesName, true),
|
||||
includeInlayPropertyDeclarationTypeHints: config.get<boolean>(InlayHintSettingNames.propertyDeclarationTypesEnabled, false),
|
||||
includeInlayFunctionLikeReturnTypeHints: config.get<boolean>(InlayHintSettingNames.functionLikeReturnTypesEnabled, false),
|
||||
includeInlayEnumMemberValueHints: config.get<boolean>(InlayHintSettingNames.enumMemberValuesEnabled, false),
|
||||
includeInlayParameterNameHints: getInlayParameterNameHintsPreference(scope, fallbackSection),
|
||||
includeInlayParameterNameHintsWhenArgumentMatchesName: !readUnifiedConfig<boolean>(InlayHintSettingNames.parameterNamesSuppressWhenArgumentMatchesName, true, { scope, fallbackSection }),
|
||||
includeInlayFunctionParameterTypeHints: readUnifiedConfig<boolean>(InlayHintSettingNames.parameterTypesEnabled, false, { scope, fallbackSection }),
|
||||
includeInlayVariableTypeHints: readUnifiedConfig<boolean>(InlayHintSettingNames.variableTypesEnabled, false, { scope, fallbackSection }),
|
||||
includeInlayVariableTypeHintsWhenTypeMatchesName: !readUnifiedConfig<boolean>(InlayHintSettingNames.variableTypesSuppressWhenTypeMatchesName, true, { scope, fallbackSection }),
|
||||
includeInlayPropertyDeclarationTypeHints: readUnifiedConfig<boolean>(InlayHintSettingNames.propertyDeclarationTypesEnabled, false, { scope, fallbackSection }),
|
||||
includeInlayFunctionLikeReturnTypeHints: readUnifiedConfig<boolean>(InlayHintSettingNames.functionLikeReturnTypesEnabled, false, { scope, fallbackSection }),
|
||||
includeInlayEnumMemberValueHints: readUnifiedConfig<boolean>(InlayHintSettingNames.enumMemberValuesEnabled, false, { scope, fallbackSection }),
|
||||
} as const;
|
||||
}
|
||||
|
||||
function getInlayParameterNameHintsPreference(config: vscode.WorkspaceConfiguration) {
|
||||
switch (config.get<string>('inlayHints.parameterNames.enabled')) {
|
||||
function getInlayParameterNameHintsPreference(scope: vscode.ConfigurationScope, fallbackSection: string) {
|
||||
switch (readUnifiedConfig<string>(InlayHintSettingNames.parameterNamesEnabled, 'none', { scope, fallbackSection })) {
|
||||
case 'none': return 'none';
|
||||
case 'literals': return 'literals';
|
||||
case 'all': return 'all';
|
||||
|
||||
@@ -11,20 +11,13 @@ import { API } from '../tsServer/api';
|
||||
import type * as Proto from '../tsServer/protocol/protocol';
|
||||
import { Location, Position } from '../typeConverters';
|
||||
import { ClientCapability, ITypeScriptServiceClient } from '../typescriptService';
|
||||
import { unifiedConfigSection } from '../utils/configuration';
|
||||
import { Disposable } from '../utils/dispose';
|
||||
import FileConfigurationManager, { InlayHintSettingNames, getInlayHintsPreferences } from './fileConfigurationManager';
|
||||
import { conditionalRegistration, requireMinVersion, requireSomeCapability } from './util/dependentRegistration';
|
||||
|
||||
|
||||
const inlayHintSettingNames = Object.freeze([
|
||||
InlayHintSettingNames.parameterNamesSuppressWhenArgumentMatchesName,
|
||||
InlayHintSettingNames.parameterNamesEnabled,
|
||||
InlayHintSettingNames.variableTypesEnabled,
|
||||
InlayHintSettingNames.variableTypesSuppressWhenTypeMatchesName,
|
||||
InlayHintSettingNames.propertyDeclarationTypesEnabled,
|
||||
InlayHintSettingNames.functionLikeReturnTypesEnabled,
|
||||
InlayHintSettingNames.enumMemberValuesEnabled,
|
||||
]);
|
||||
const inlayHintSettingNames = Object.values(InlayHintSettingNames);
|
||||
|
||||
class TypeScriptInlayHintsProvider extends Disposable implements vscode.InlayHintsProvider {
|
||||
|
||||
@@ -44,7 +37,10 @@ class TypeScriptInlayHintsProvider extends Disposable implements vscode.InlayHin
|
||||
super();
|
||||
|
||||
this._register(vscode.workspace.onDidChangeConfiguration(e => {
|
||||
if (inlayHintSettingNames.some(settingName => e.affectsConfiguration(language.id + '.' + settingName))) {
|
||||
if (inlayHintSettingNames.some(settingName =>
|
||||
e.affectsConfiguration(unifiedConfigSection + '.' + settingName) ||
|
||||
e.affectsConfiguration(language.id + '.' + settingName)
|
||||
)) {
|
||||
this._onDidChangeInlayHints.fire();
|
||||
}
|
||||
}));
|
||||
@@ -131,8 +127,7 @@ function fromProtocolInlayHintKind(kind: Proto.InlayHintKind): vscode.InlayHintK
|
||||
}
|
||||
|
||||
function areInlayHintsEnabledForFile(language: LanguageDescription, document: vscode.TextDocument) {
|
||||
const config = vscode.workspace.getConfiguration(language.id, document);
|
||||
const preferences = getInlayHintsPreferences(config);
|
||||
const preferences = getInlayHintsPreferences(document, language.id);
|
||||
|
||||
return preferences.includeInlayParameterNameHints === 'literals' ||
|
||||
preferences.includeInlayParameterNameHints === 'all' ||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { parse as parseJSONC } from '../../../../../base/common/jsonc.js';
|
||||
import { setProperty, applyEdits } from '../../../../../base/common/jsonEdit.js';
|
||||
import { FormattingOptions } from '../../../../../base/common/jsonFormatter.js';
|
||||
import { isEqual } from '../../../../../base/common/resources.js';
|
||||
import { URI } from '../../../../../base/common/uri.js';
|
||||
import { VSBuffer } from '../../../../../base/common/buffer.js';
|
||||
@@ -102,7 +105,7 @@ async function addHookToFile(
|
||||
if (fileExists) {
|
||||
const existingContent = await fileService.readFile(hookFileUri);
|
||||
try {
|
||||
hooksContent = JSON.parse(existingContent.value.toString());
|
||||
hooksContent = parseJSONC(existingContent.value.toString());
|
||||
// Ensure hooks object exists
|
||||
if (!hooksContent.hooks) {
|
||||
hooksContent.hooks = {};
|
||||
@@ -144,19 +147,25 @@ async function addHookToFile(
|
||||
// Use existing key if found, otherwise use the detected naming convention
|
||||
const keyToUse = existingKeyForType ?? hookTypeKeyName;
|
||||
|
||||
// Add the new hook entry (append if hook type already exists)
|
||||
// Determine the new hook index (append if hook type already exists)
|
||||
const newHookEntry = buildNewHookEntry(sourceFormat);
|
||||
let newHookIndex: number;
|
||||
if (!hooksContent.hooks[keyToUse]) {
|
||||
hooksContent.hooks[keyToUse] = [newHookEntry];
|
||||
newHookIndex = 0;
|
||||
} else {
|
||||
hooksContent.hooks[keyToUse].push(newHookEntry);
|
||||
newHookIndex = hooksContent.hooks[keyToUse].length - 1;
|
||||
}
|
||||
const existingHooks = hooksContent.hooks[keyToUse];
|
||||
const newHookIndex = Array.isArray(existingHooks) ? existingHooks.length : 0;
|
||||
|
||||
// Write the file
|
||||
const jsonContent = JSON.stringify(hooksContent, null, '\t');
|
||||
// Generate the new JSON content using setProperty to preserve comments
|
||||
let jsonContent: string;
|
||||
if (fileExists) {
|
||||
// Use setProperty to make targeted edits that preserve comments
|
||||
const originalText = (await fileService.readFile(hookFileUri)).value.toString();
|
||||
const detectedEol = originalText.includes('\r\n') ? '\r\n' : '\n';
|
||||
const formattingOptions: FormattingOptions = { tabSize: 1, insertSpaces: false, eol: detectedEol };
|
||||
const edits = setProperty(originalText, ['hooks', keyToUse, newHookIndex], newHookEntry, formattingOptions);
|
||||
jsonContent = applyEdits(originalText, edits);
|
||||
} else {
|
||||
// New file - use JSON.stringify since there are no comments to preserve
|
||||
const newContent = { hooks: { [keyToUse]: [newHookEntry] } };
|
||||
jsonContent = JSON.stringify(newContent, null, '\t');
|
||||
}
|
||||
|
||||
// Check if the file is already open in an editor
|
||||
const existingEditor = editorService.editors.find(e => isEqual(e.resource, hookFileUri));
|
||||
|
||||
@@ -128,28 +128,9 @@ export function parseClaudeHooks(
|
||||
const commands: IHookCommand[] = [];
|
||||
|
||||
for (const item of hookArray) {
|
||||
if (!item || typeof item !== 'object') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const itemObj = item as Record<string, unknown>;
|
||||
|
||||
// Claude can have nested hooks with matchers: { matcher: "Bash", hooks: [...] }
|
||||
const nestedHooks = (itemObj as { hooks?: unknown }).hooks;
|
||||
if (nestedHooks !== undefined && Array.isArray(nestedHooks)) {
|
||||
for (const nestedHook of nestedHooks) {
|
||||
const resolved = resolveClaudeCommand(nestedHook as Record<string, unknown>, workspaceRootUri, userHome);
|
||||
if (resolved) {
|
||||
commands.push(resolved);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Direct hook command
|
||||
const resolved = resolveClaudeCommand(itemObj, workspaceRootUri, userHome);
|
||||
if (resolved) {
|
||||
commands.push(resolved);
|
||||
}
|
||||
}
|
||||
// Use shared helper that handles both direct commands and nested matcher structures
|
||||
const extracted = extractHookCommandsFromItem(item, workspaceRootUri, userHome);
|
||||
commands.push(...extracted);
|
||||
}
|
||||
|
||||
if (commands.length > 0) {
|
||||
@@ -166,19 +147,59 @@ export function parseClaudeHooks(
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a Claude hook command to our IHookCommand format.
|
||||
* Claude commands can be: { type: "command", command: "..." } or { command: "..." }
|
||||
* Helper to extract hook commands from an item that could be:
|
||||
* 1. A direct command object: { type: 'command', command: '...' }
|
||||
* 2. A nested structure with matcher (Claude style): { matcher: '...', hooks: [{ type: 'command', command: '...' }] }
|
||||
*
|
||||
* This allows Copilot format to handle Claude-style entries if pasted.
|
||||
* Also handles Claude's leniency where 'type' field can be omitted.
|
||||
*/
|
||||
function resolveClaudeCommand(
|
||||
raw: Record<string, unknown>,
|
||||
export function extractHookCommandsFromItem(
|
||||
item: unknown,
|
||||
workspaceRootUri: URI | undefined,
|
||||
userHome: string
|
||||
): IHookCommand | undefined {
|
||||
// Claude might not require 'type' field, so we're more lenient
|
||||
const hasValidType = raw.type === undefined || raw.type === 'command';
|
||||
if (!hasValidType) {
|
||||
return undefined;
|
||||
): IHookCommand[] {
|
||||
if (!item || typeof item !== 'object') {
|
||||
return [];
|
||||
}
|
||||
|
||||
return resolveHookCommand(raw, workspaceRootUri, userHome);
|
||||
const itemObj = item as Record<string, unknown>;
|
||||
const commands: IHookCommand[] = [];
|
||||
|
||||
// Check for nested hooks with matcher (Claude style): { matcher: "...", hooks: [...] }
|
||||
const nestedHooks = itemObj.hooks;
|
||||
if (nestedHooks !== undefined && Array.isArray(nestedHooks)) {
|
||||
for (const nestedHook of nestedHooks) {
|
||||
if (!nestedHook || typeof nestedHook !== 'object') {
|
||||
continue;
|
||||
}
|
||||
const normalized = normalizeForResolve(nestedHook as Record<string, unknown>);
|
||||
const resolved = resolveHookCommand(normalized, workspaceRootUri, userHome);
|
||||
if (resolved) {
|
||||
commands.push(resolved);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Direct command object
|
||||
const normalized = normalizeForResolve(itemObj);
|
||||
const resolved = resolveHookCommand(normalized, workspaceRootUri, userHome);
|
||||
if (resolved) {
|
||||
commands.push(resolved);
|
||||
}
|
||||
}
|
||||
|
||||
return commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a hook command object for resolving.
|
||||
* Claude format allows omitting the 'type' field, treating it as 'command'.
|
||||
* This ensures compatibility when Claude-style hooks are pasted into Copilot format.
|
||||
*/
|
||||
function normalizeForResolve(raw: Record<string, unknown>): Record<string, unknown> {
|
||||
// If type is missing or already 'command', ensure it's set to 'command'
|
||||
if (raw.type === undefined || raw.type === 'command') {
|
||||
return { ...raw, type: 'command' };
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
import { URI } from '../../../../../base/common/uri.js';
|
||||
import { basename, dirname } from '../../../../../base/common/path.js';
|
||||
import { HookType, IHookCommand, toHookType, resolveHookCommand } from './hookSchema.js';
|
||||
import { parseClaudeHooks } from './hookClaudeCompat.js';
|
||||
import { HookType, IHookCommand, toHookType } from './hookSchema.js';
|
||||
import { parseClaudeHooks, extractHookCommandsFromItem } from './hookClaudeCompat.js';
|
||||
import { resolveCopilotCliHookType } from './hookCopilotCliCompat.js';
|
||||
|
||||
/**
|
||||
@@ -97,10 +97,9 @@ export function parseCopilotHooks(
|
||||
const commands: IHookCommand[] = [];
|
||||
|
||||
for (const item of hookArray) {
|
||||
const resolved = resolveHookCommand(item as Record<string, unknown>, workspaceRootUri, userHome);
|
||||
if (resolved) {
|
||||
commands.push(resolved);
|
||||
}
|
||||
// Use helper that handles both direct commands and Claude-style nested matcher structures
|
||||
const extracted = extractHookCommandsFromItem(item, workspaceRootUri, userHome);
|
||||
commands.push(...extracted);
|
||||
}
|
||||
|
||||
if (commands.length > 0) {
|
||||
|
||||
@@ -6,13 +6,98 @@
|
||||
import assert from 'assert';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js';
|
||||
import { HookType } from '../../../common/promptSyntax/hookSchema.js';
|
||||
import { parseClaudeHooks, resolveClaudeHookType, getClaudeHookTypeName } from '../../../common/promptSyntax/hookClaudeCompat.js';
|
||||
import { parseClaudeHooks, resolveClaudeHookType, getClaudeHookTypeName, extractHookCommandsFromItem } from '../../../common/promptSyntax/hookClaudeCompat.js';
|
||||
import { getHookSourceFormat, HookSourceFormat, buildNewHookEntry } from '../../../common/promptSyntax/hookCompatibility.js';
|
||||
import { URI } from '../../../../../../base/common/uri.js';
|
||||
|
||||
suite('HookClaudeCompat', () => {
|
||||
ensureNoDisposablesAreLeakedInTestSuite();
|
||||
|
||||
suite('extractHookCommandsFromItem', () => {
|
||||
const workspaceRoot = URI.file('/workspace');
|
||||
const userHome = '/home/user';
|
||||
|
||||
test('extracts direct command object', () => {
|
||||
const item = { type: 'command', command: 'echo "test"' };
|
||||
|
||||
const result = extractHookCommandsFromItem(item, workspaceRoot, userHome);
|
||||
|
||||
assert.strictEqual(result.length, 1);
|
||||
assert.strictEqual(result[0].command, 'echo "test"');
|
||||
});
|
||||
|
||||
test('extracts from nested matcher structure', () => {
|
||||
const item = {
|
||||
matcher: 'Bash',
|
||||
hooks: [
|
||||
{ type: 'command', command: 'echo "nested"' }
|
||||
]
|
||||
};
|
||||
|
||||
const result = extractHookCommandsFromItem(item, workspaceRoot, userHome);
|
||||
|
||||
assert.strictEqual(result.length, 1);
|
||||
assert.strictEqual(result[0].command, 'echo "nested"');
|
||||
});
|
||||
|
||||
test('extracts multiple hooks from matcher structure', () => {
|
||||
const item = {
|
||||
matcher: 'Write',
|
||||
hooks: [
|
||||
{ type: 'command', command: 'echo "first"' },
|
||||
{ type: 'command', command: 'echo "second"' }
|
||||
]
|
||||
};
|
||||
|
||||
const result = extractHookCommandsFromItem(item, workspaceRoot, userHome);
|
||||
|
||||
assert.strictEqual(result.length, 2);
|
||||
assert.strictEqual(result[0].command, 'echo "first"');
|
||||
assert.strictEqual(result[1].command, 'echo "second"');
|
||||
});
|
||||
|
||||
test('handles command without type field (Claude format)', () => {
|
||||
const item = { command: 'echo "no type"' };
|
||||
|
||||
const result = extractHookCommandsFromItem(item, workspaceRoot, userHome);
|
||||
|
||||
assert.strictEqual(result.length, 1);
|
||||
assert.strictEqual(result[0].command, 'echo "no type"');
|
||||
});
|
||||
|
||||
test('handles nested command without type field', () => {
|
||||
const item = {
|
||||
matcher: 'Bash',
|
||||
hooks: [
|
||||
{ command: 'echo "no type nested"' }
|
||||
]
|
||||
};
|
||||
|
||||
const result = extractHookCommandsFromItem(item, workspaceRoot, userHome);
|
||||
|
||||
assert.strictEqual(result.length, 1);
|
||||
assert.strictEqual(result[0].command, 'echo "no type nested"');
|
||||
});
|
||||
|
||||
test('returns empty array for null item', () => {
|
||||
const result = extractHookCommandsFromItem(null, workspaceRoot, userHome);
|
||||
assert.strictEqual(result.length, 0);
|
||||
});
|
||||
|
||||
test('returns empty array for undefined item', () => {
|
||||
const result = extractHookCommandsFromItem(undefined, workspaceRoot, userHome);
|
||||
assert.strictEqual(result.length, 0);
|
||||
});
|
||||
|
||||
test('returns empty array for invalid type', () => {
|
||||
const item = { type: 'script', command: 'echo "wrong type"' };
|
||||
|
||||
const result = extractHookCommandsFromItem(item, workspaceRoot, userHome);
|
||||
|
||||
assert.strictEqual(result.length, 0);
|
||||
});
|
||||
});
|
||||
|
||||
suite('resolveClaudeHookType', () => {
|
||||
test('resolves PreToolUse', () => {
|
||||
assert.strictEqual(resolveClaudeHookType('PreToolUse'), HookType.PreToolUse);
|
||||
|
||||
@@ -52,6 +52,94 @@ suite('HookCompatibility', () => {
|
||||
assert.strictEqual(result.size, 0);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Claude-style matcher compatibility', () => {
|
||||
test('parses Claude-style nested matcher structure', () => {
|
||||
// When Claude format is pasted into Copilot hooks file
|
||||
const json = {
|
||||
hooks: {
|
||||
PreToolUse: [
|
||||
{
|
||||
matcher: 'Bash',
|
||||
hooks: [
|
||||
{ type: 'command', command: 'echo "from matcher"' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const result = parseCopilotHooks(json, workspaceRoot, userHome);
|
||||
|
||||
assert.strictEqual(result.size, 1);
|
||||
const entry = result.get(HookType.PreToolUse)!;
|
||||
assert.strictEqual(entry.hooks.length, 1);
|
||||
assert.strictEqual(entry.hooks[0].command, 'echo "from matcher"');
|
||||
});
|
||||
|
||||
test('parses Claude-style nested matcher with multiple hooks', () => {
|
||||
const json = {
|
||||
hooks: {
|
||||
PostToolUse: [
|
||||
{
|
||||
matcher: 'Write',
|
||||
hooks: [
|
||||
{ type: 'command', command: 'echo "first"' },
|
||||
{ type: 'command', command: 'echo "second"' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const result = parseCopilotHooks(json, workspaceRoot, userHome);
|
||||
|
||||
const entry = result.get(HookType.PostToolUse)!;
|
||||
assert.strictEqual(entry.hooks.length, 2);
|
||||
assert.strictEqual(entry.hooks[0].command, 'echo "first"');
|
||||
assert.strictEqual(entry.hooks[1].command, 'echo "second"');
|
||||
});
|
||||
|
||||
test('handles mixed direct and nested matcher entries', () => {
|
||||
const json = {
|
||||
hooks: {
|
||||
PreToolUse: [
|
||||
{ type: 'command', command: 'echo "direct"' },
|
||||
{
|
||||
matcher: 'Bash',
|
||||
hooks: [
|
||||
{ type: 'command', command: 'echo "nested"' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const result = parseCopilotHooks(json, workspaceRoot, userHome);
|
||||
|
||||
const entry = result.get(HookType.PreToolUse)!;
|
||||
assert.strictEqual(entry.hooks.length, 2);
|
||||
assert.strictEqual(entry.hooks[0].command, 'echo "direct"');
|
||||
assert.strictEqual(entry.hooks[1].command, 'echo "nested"');
|
||||
});
|
||||
|
||||
test('handles Claude-style hook without type field', () => {
|
||||
// Claude allows omitting the type field
|
||||
const json = {
|
||||
hooks: {
|
||||
SessionStart: [
|
||||
{ command: 'echo "no type"' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const result = parseCopilotHooks(json, workspaceRoot, userHome);
|
||||
|
||||
const entry = result.get(HookType.SessionStart)!;
|
||||
assert.strictEqual(entry.hooks.length, 1);
|
||||
assert.strictEqual(entry.hooks[0].command, 'echo "no type"');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('parseHooksFromFile', () => {
|
||||
|
||||
Reference in New Issue
Block a user