Move more config into local typescript gulpfile
This commit is contained in:
@@ -3,7 +3,7 @@ module.exports = {
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: "module",
|
||||
project: ["tsconfig.json", "./src/**/tsconfig.json"],
|
||||
project: ["tsconfig.json", "./src/**/tsconfig.json", "./_gulpfile.ts/tsconfig.json"],
|
||||
},
|
||||
plugins: ["@typescript-eslint"],
|
||||
env: {
|
||||
|
||||
201
extensions/ql-vscode/_gulpfile.ts/deploy.ts
Normal file
201
extensions/ql-vscode/_gulpfile.ts/deploy.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as jsonc from 'jsonc-parser';
|
||||
import { IPackageJson } from '@microsoft/node-core-library';
|
||||
import * as path from 'path';
|
||||
import { getRushContext, RushContext } from './rush';
|
||||
import * as packlist from 'npm-packlist';
|
||||
import * as glob from 'glob-promise';
|
||||
import * as cpp from 'child-process-promise';
|
||||
|
||||
interface PackageInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
sourcePath: string;
|
||||
files: string[];
|
||||
dependencies: PackageInfo[];
|
||||
isRoot?: boolean;
|
||||
copied?: boolean;
|
||||
}
|
||||
|
||||
async function copyPackage(packageFiles: PackageInfo, destPath: string): Promise<void> {
|
||||
for (const file of packageFiles.files) {
|
||||
const sourceFilePath = path.resolve(packageFiles.sourcePath, file);
|
||||
const destFilePath = path.resolve(destPath, file);
|
||||
if (packageFiles.isRoot && (file === 'package.json')) {
|
||||
// For non-release builds, we tweak the version number of the extension to add a prerelease
|
||||
// suffix. Rather than just copying `package.json`, we'll parse the original copy, update the
|
||||
// `version` property, and write it out to the new location.
|
||||
const packageJson = jsonc.parse((await fs.readFile(sourceFilePath)).toString());
|
||||
packageJson.version = packageFiles.version;
|
||||
await fs.writeFile(destFilePath, JSON.stringify(packageJson));
|
||||
}
|
||||
else {
|
||||
await fs.copy(sourceFilePath, destFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface DeployedPackage {
|
||||
distPath: string;
|
||||
name: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
class PackageMap {
|
||||
private map = new Map<string, Map<string, PackageInfo>>();
|
||||
|
||||
public getPackageInfo(name: string, version: string): PackageInfo | undefined {
|
||||
const versionMap = this.map.get(name);
|
||||
if (versionMap === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return versionMap.get(version);
|
||||
}
|
||||
|
||||
public addPackageInfo(pkg: PackageInfo): void {
|
||||
if (this.getPackageInfo(pkg.name, pkg.version)) {
|
||||
throw new Error(`Attempt to add duplicate package '${pkg.name}@${pkg.version}'.`);
|
||||
}
|
||||
|
||||
let versionMap = this.map.get(pkg.name);
|
||||
if (versionMap === undefined) {
|
||||
versionMap = new Map<string, PackageInfo>();
|
||||
this.map.set(pkg.name, versionMap);
|
||||
}
|
||||
|
||||
versionMap.set(pkg.version, pkg);
|
||||
}
|
||||
|
||||
public hasMultipleVersions(name: string): boolean {
|
||||
return this.map.get(name)!.size > 1;
|
||||
}
|
||||
}
|
||||
|
||||
async function collectPackages(context: RushContext, name: string, version: string,
|
||||
pkgs: PackageMap): Promise<PackageInfo> {
|
||||
|
||||
let pkg = pkgs.getPackageInfo(name, version);
|
||||
if (!pkg) {
|
||||
const info = await context.getPackageInfo(name, version);
|
||||
|
||||
let files: string[];
|
||||
if (info.isLocal) {
|
||||
// For local packages, use `packlist` to get the list of files that npm would have packed
|
||||
// into the tarball.
|
||||
files = packlist.sync({ path: info.path });
|
||||
}
|
||||
else {
|
||||
// For non-local packages, just copy everything.
|
||||
files = await glob('**/*', {
|
||||
nodir: true,
|
||||
cwd: info.path
|
||||
});
|
||||
}
|
||||
|
||||
pkg = {
|
||||
name: name,
|
||||
version: version,
|
||||
sourcePath: info.path,
|
||||
files: files,
|
||||
dependencies: []
|
||||
};
|
||||
|
||||
pkgs.addPackageInfo(pkg);
|
||||
|
||||
for (const dependencyName of info.dependencies.keys()) {
|
||||
const dependencyVersion = info.dependencies.get(dependencyName)!;
|
||||
|
||||
const dependencyPackage = await collectPackages(context, dependencyName, dependencyVersion, pkgs);
|
||||
pkg.dependencies.push(dependencyPackage);
|
||||
}
|
||||
}
|
||||
|
||||
return pkg;
|
||||
}
|
||||
|
||||
async function copyPackageAndModules(pkg: PackageInfo, pkgs: PackageMap, destPath: string,
|
||||
rootNodeModulesPath: string): Promise<void> {
|
||||
|
||||
let destPackagePath: string;
|
||||
if (pkgs.hasMultipleVersions(pkg.name) || pkg.isRoot) {
|
||||
// Copy as a nested package, and let `npm dedupe` fix it up later if possible.
|
||||
destPackagePath = path.join(destPath, pkg.name);
|
||||
}
|
||||
else {
|
||||
// Copy to the root `node_modules` directory.
|
||||
if (pkg.copied) {
|
||||
return;
|
||||
}
|
||||
pkg.copied = true;
|
||||
destPackagePath = path.join(rootNodeModulesPath, pkg.name);
|
||||
}
|
||||
|
||||
await copyPackage(pkg, destPackagePath);
|
||||
const nodeModulesPath = path.join(destPackagePath, 'node_modules');
|
||||
for (const dependencyPkg of pkg.dependencies) {
|
||||
await copyPackageAndModules(dependencyPkg, pkgs, nodeModulesPath, rootNodeModulesPath);
|
||||
}
|
||||
}
|
||||
|
||||
export async function deployPackage(packageJsonPath: string): Promise<DeployedPackage> {
|
||||
try {
|
||||
const context = await getRushContext(path.dirname(packageJsonPath));
|
||||
|
||||
const rootPackage: IPackageJson = jsonc.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
||||
|
||||
// Default to development build; use flag --release to indicate release build.
|
||||
const isDevBuild = !process.argv.includes('--release');
|
||||
const distDir = path.join(context.rushConfig.rushJsonFolder, 'dist');
|
||||
await fs.mkdirs(distDir);
|
||||
|
||||
if (isDevBuild) {
|
||||
// NOTE: rootPackage.name had better not have any regex metacharacters
|
||||
const oldDevBuildPattern = new RegExp('^' + rootPackage.name + '[^/]+-dev[0-9.]+\\.vsix$');
|
||||
// Dev package filenames are of the form
|
||||
// vscode-codeql-0.0.1-dev.2019.9.27.19.55.20.vsix
|
||||
(await fs.readdir(distDir)).filter(name => name.match(oldDevBuildPattern)).map(build => {
|
||||
console.log(`Deleting old dev build ${build}...`);
|
||||
fs.unlinkSync(path.join(distDir, build));
|
||||
});
|
||||
const now = new Date();
|
||||
rootPackage.version = rootPackage.version +
|
||||
`-dev.${now.getUTCFullYear()}.${now.getUTCMonth() + 1}.${now.getUTCDate()}` +
|
||||
`.${now.getUTCHours()}.${now.getUTCMinutes()}.${now.getUTCSeconds()}`;
|
||||
}
|
||||
|
||||
const distPath = path.join(distDir, rootPackage.name);
|
||||
await fs.remove(distPath);
|
||||
await fs.mkdirs(distPath);
|
||||
|
||||
console.log(`Gathering transitive dependencies of package '${rootPackage.name}'...`);
|
||||
const pkgs = new PackageMap();
|
||||
const rootPkg = await collectPackages(context, rootPackage.name, rootPackage.version, pkgs);
|
||||
rootPkg.isRoot = true;
|
||||
|
||||
console.log(`Copying package '${rootPackage.name}' and its dependencies to '${distPath}'...`);
|
||||
await copyPackageAndModules(rootPkg, pkgs, path.dirname(distPath), path.join(distPath, 'node_modules'));
|
||||
await fs.copy(path.resolve(rootPkg.sourcePath, '.vscodeignore'), path.resolve(distPath, '.vscodeignore'));
|
||||
|
||||
console.log(`Deduplicating dependencies of package '${rootPackage.name}'...`);
|
||||
// We create a temporary `package-lock.json` file just to prevent `npm ls` from printing out the
|
||||
// message that it created a package-lock.json.
|
||||
const packageLockPath = path.join(distPath, 'package-lock.json');
|
||||
await fs.writeFile(packageLockPath, '{}');
|
||||
await cpp.spawn('npm', ['dedupe'], {
|
||||
cwd: distPath,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
await fs.unlink(packageLockPath);
|
||||
|
||||
return {
|
||||
distPath: distPath,
|
||||
name: rootPackage.name,
|
||||
version: rootPackage.version
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
const task = (done: any) => {
|
||||
console.log('Hello world!');
|
||||
done();
|
||||
};
|
||||
import * as gulp from 'gulp';
|
||||
import { compileTypeScript, watchTypeScript, copyViewCss } from './typescript';
|
||||
import { compileTextMateGrammar } from './textmate';
|
||||
import { copyTestData } from './tests';
|
||||
import { compileView } from './webpack';
|
||||
|
||||
|
||||
export default task;
|
||||
export const buildWithoutPackage = gulp.parallel(compileTypeScript, compileTextMateGrammar, compileView, copyTestData, copyViewCss);
|
||||
export { compileTextMateGrammar, watchTypeScript, compileTypeScript };
|
||||
// exports.default = gulp.series(exports.buildWithoutPackage, packageExtension);
|
||||
|
||||
23
extensions/ql-vscode/_gulpfile.ts/package.ts
Normal file
23
extensions/ql-vscode/_gulpfile.ts/package.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import * as path from 'path';
|
||||
import { deployPackage } from './deploy';
|
||||
import * as childProcess from 'child-process-promise';
|
||||
|
||||
export async function packageExtension(): Promise<void> {
|
||||
const deployedPackage = await deployPackage(path.resolve('package.json'));
|
||||
console.log(`Packaging extension '${deployedPackage.name}@${deployedPackage.version}'...`);
|
||||
const args = [
|
||||
'package',
|
||||
'--out', path.resolve(deployedPackage.distPath, '..', `${deployedPackage.name}-${deployedPackage.version}.vsix`)
|
||||
];
|
||||
const proc = childProcess.spawn('vsce', args, {
|
||||
cwd: deployedPackage.distPath
|
||||
});
|
||||
proc.childProcess.stdout!.on('data', (data) => {
|
||||
console.log(data.toString());
|
||||
});
|
||||
proc.childProcess.stderr!.on('data', (data) => {
|
||||
console.error(data.toString());
|
||||
});
|
||||
|
||||
await proc;
|
||||
}
|
||||
17
extensions/ql-vscode/_gulpfile.ts/pnpm.ts
Normal file
17
extensions/ql-vscode/_gulpfile.ts/pnpm.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export interface PackageDependencies {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export interface ShrinkwrapPackage {
|
||||
dependencies?: PackageDependencies;
|
||||
dev?: boolean;
|
||||
name?: string;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export interface Shrinkwrap {
|
||||
dependencies: PackageDependencies;
|
||||
packages: {
|
||||
[key: string]: ShrinkwrapPackage;
|
||||
};
|
||||
}
|
||||
166
extensions/ql-vscode/_gulpfile.ts/rush.ts
Normal file
166
extensions/ql-vscode/_gulpfile.ts/rush.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import * as glob from 'glob-promise';
|
||||
import * as jsonc from 'jsonc-parser';
|
||||
import { Shrinkwrap, ShrinkwrapPackage } from './pnpm';
|
||||
import * as path from 'path';
|
||||
import { IPackageJson } from '@microsoft/node-core-library';
|
||||
import { RushConfiguration } from '@microsoft/rush-lib';
|
||||
import * as yaml from 'js-yaml';
|
||||
|
||||
export interface PackageJsonWithFiles extends IPackageJson {
|
||||
files?: string[];
|
||||
}
|
||||
|
||||
interface PackageInfo {
|
||||
path: string;
|
||||
dependencies: Map<string, string>;
|
||||
config: PackageJsonWithFiles;
|
||||
isLocal: boolean;
|
||||
}
|
||||
|
||||
const peerDependencyVersionPattern = /^\/((?:@(?:[^\/]+)\/)?[^\/]+)\/([^\/]+)\//;
|
||||
|
||||
export class RushContext {
|
||||
private shrinkwrap?: Shrinkwrap;
|
||||
private shrinkwrapPackages?: Map<string, ShrinkwrapPackage>;
|
||||
private readonly packageStore: string;
|
||||
|
||||
constructor(public readonly rushConfig: RushConfiguration) {
|
||||
this.packageStore = path.join(rushConfig.pnpmStoreFolder, '2');
|
||||
}
|
||||
|
||||
private async findPackageInRepository(name: string, version: string): Promise<string> {
|
||||
// Packages may be pulled from multiple registries, each of which has its own directory in the
|
||||
// pnpm store. Search for the package name in any of these directories. We use `*.*` to match
|
||||
// the directory name to avoid searching the `local` directory, which does not represent a
|
||||
// package registry.
|
||||
const results = await glob(`*.*/${name}/${version}/package`, {
|
||||
absolute: true,
|
||||
cwd: this.packageStore
|
||||
});
|
||||
if (results.length === 0) {
|
||||
throw new Error(`Package '${name}:${version}' not found in package repository.`);
|
||||
}
|
||||
else if (results.length > 1) {
|
||||
throw new Error(`Multiple copies of package '${name}:${version}' found in package repository.`);
|
||||
}
|
||||
else {
|
||||
return results[0];
|
||||
}
|
||||
}
|
||||
|
||||
private getRushProjectPath(name: string): string | undefined {
|
||||
const project = this.rushConfig.getProjectByName(name);
|
||||
if (project) {
|
||||
return project.projectFolder;
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async getShrinkwrap(): Promise<Shrinkwrap> {
|
||||
if (!this.shrinkwrap) {
|
||||
this.shrinkwrap = yaml.safeLoad(await fs.readFile(this.rushConfig.getCommittedShrinkwrapFilename(), 'utf8'));
|
||||
}
|
||||
|
||||
return this.shrinkwrap!;
|
||||
}
|
||||
|
||||
private async getShrinkwrapPackage(name: string, version: string): Promise<ShrinkwrapPackage> {
|
||||
const shrinkwrap = await this.getShrinkwrap();
|
||||
|
||||
if (!this.shrinkwrapPackages) {
|
||||
this.shrinkwrapPackages = new Map<string, ShrinkwrapPackage>();
|
||||
for (const name in shrinkwrap.packages) {
|
||||
const pkg = shrinkwrap.packages[name];
|
||||
let packageKey: string;
|
||||
if (pkg.name) {
|
||||
packageKey = makePackageKey(pkg.name, pkg.version!);
|
||||
}
|
||||
else {
|
||||
packageKey = name;
|
||||
}
|
||||
this.shrinkwrapPackages.set(packageKey, pkg);
|
||||
}
|
||||
}
|
||||
|
||||
const packageKey = makePackageKey(name, version);
|
||||
const shrinkwrapPackage = this.shrinkwrapPackages.get(packageKey);
|
||||
if (!shrinkwrapPackage) {
|
||||
throw new Error(`Package '${packageKey}' not found in shrinkwrap file.`);
|
||||
}
|
||||
return shrinkwrapPackage;
|
||||
}
|
||||
|
||||
public async getPackageInfo(name: string, version: string): Promise<PackageInfo> {
|
||||
let pkg: ShrinkwrapPackage;
|
||||
const rushProject = this.rushConfig.getProjectByName(name);
|
||||
let packagePath: string;
|
||||
let config: PackageJsonWithFiles;
|
||||
if (rushProject) {
|
||||
packagePath = rushProject.projectFolder;
|
||||
pkg = await this.getShrinkwrapPackage(rushProject.tempProjectName, '0.0.0');
|
||||
config = rushProject.packageJson;
|
||||
}
|
||||
else {
|
||||
pkg = await this.getShrinkwrapPackage(name, version);
|
||||
// Ensure a proper version number. pnpm uses syntax like 3.4.0_glob@7.1.6 for peer dependencies
|
||||
version = version.split('_')[0];
|
||||
packagePath = await this.findPackageInRepository(name, version);
|
||||
packagePath = await fs.realpath(packagePath);
|
||||
config = jsonc.parse(await fs.readFile(path.join(packagePath, 'package.json'), 'utf8'));
|
||||
}
|
||||
|
||||
const dependencies = new Map<string, string>();
|
||||
if (config.dependencies) {
|
||||
for (const dependencyName in config.dependencies) {
|
||||
let dependencyVersion: string;
|
||||
if (await this.getRushProjectPath(dependencyName)) {
|
||||
dependencyVersion = '0.0.0';
|
||||
}
|
||||
else {
|
||||
dependencyVersion = pkg.dependencies![dependencyName];
|
||||
if (!dependencyVersion) {
|
||||
throw new Error(`Package '${name}' depends on unresolved package '${dependencyName}'.`);
|
||||
}
|
||||
if (dependencyVersion.startsWith('/')) {
|
||||
// This is a package with a peer dependency. We need to extract the actual package
|
||||
// version.
|
||||
const match = dependencyVersion.match(peerDependencyVersionPattern);
|
||||
if (match) {
|
||||
if (match[1] !== dependencyName) {
|
||||
throw new Error(`Mismatch between package name '${dependencyName}' and peer dependency specifier '${dependencyVersion}'.`);
|
||||
}
|
||||
dependencyVersion = match[2];
|
||||
}
|
||||
else {
|
||||
throw new Error(`Invalid peer dependency specifier '${dependencyVersion}'.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies.set(dependencyName, dependencyVersion);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
path: packagePath,
|
||||
dependencies: dependencies,
|
||||
config: config,
|
||||
isLocal: rushProject !== undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function makePackageKey(name: string, version: string): string {
|
||||
return `/${name}/${version}`;
|
||||
}
|
||||
|
||||
export async function getRushContext(startingFolder?: string): Promise<RushContext> {
|
||||
const rushConfig = RushConfiguration.loadFromDefaultLocation({
|
||||
startingFolder: startingFolder
|
||||
});
|
||||
|
||||
return new RushContext(rushConfig);
|
||||
}
|
||||
6
extensions/ql-vscode/_gulpfile.ts/tests.ts
Normal file
6
extensions/ql-vscode/_gulpfile.ts/tests.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import * as gulp from 'gulp';
|
||||
|
||||
export function copyTestData() {
|
||||
return gulp.src('src/vscode-tests/no-workspace/data/**/*')
|
||||
.pipe(gulp.dest('out/vscode-tests/no-workspace/data'));
|
||||
}
|
||||
246
extensions/ql-vscode/_gulpfile.ts/textmate.ts
Normal file
246
extensions/ql-vscode/_gulpfile.ts/textmate.ts
Normal file
@@ -0,0 +1,246 @@
|
||||
import * as gulp from 'gulp';
|
||||
import * as jsYaml from 'js-yaml';
|
||||
import * as through from 'through2';
|
||||
import * as PluginError from 'plugin-error';
|
||||
import * as Vinyl from 'vinyl';
|
||||
|
||||
/**
|
||||
* Replaces all rule references with the match pattern of the referenced rule.
|
||||
*
|
||||
* @param value Original regex containing rule references.
|
||||
* @param replacements Map from rule name to match text.
|
||||
* @returns The new regex after replacement.
|
||||
*/
|
||||
function replaceReferencesWithStrings(value: string, replacements: Map<string, string>): string {
|
||||
let result = value;
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const original = result;
|
||||
for (const key of Array.from(replacements.keys())) {
|
||||
result = result.replace(`(?#${key})`, `(?:${replacements.get(key)})`);
|
||||
}
|
||||
if (result === original) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather all macro definitions from the document.
|
||||
*
|
||||
* @param yaml The root of the YAML document.
|
||||
* @returns A map from macro name to replacement text.
|
||||
*/
|
||||
function gatherMacros(yaml: any): Map<string, string> {
|
||||
const macros = new Map<string, string>();
|
||||
for (const key in yaml.macros) {
|
||||
macros.set(key, yaml.macros[key]);
|
||||
}
|
||||
|
||||
return macros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the match text to be substituted wherever the specified rule is referenced in a regular
|
||||
* expression.
|
||||
*
|
||||
* @param rule The rule whose match text is to be retrieved.
|
||||
* @returns The match text for the rule. This is either the value of the rule's `match` property,
|
||||
* or the disjunction of the match text of all of the other rules `include`d by this rule.
|
||||
*/
|
||||
function getNodeMatchText(rule: any): string {
|
||||
if (rule.match !== undefined) {
|
||||
// For a match string, just use that string as the replacement.
|
||||
return rule.match;
|
||||
}
|
||||
else if (rule.patterns !== undefined) {
|
||||
const patterns: string[] = [];
|
||||
// For a list of patterns, use the disjunction of those patterns.
|
||||
for (const patternIndex in rule.patterns) {
|
||||
const pattern = rule.patterns[patternIndex];
|
||||
if (pattern.include !== null) {
|
||||
patterns.push('(?' + pattern.include + ')');
|
||||
}
|
||||
}
|
||||
|
||||
return '(?:' + patterns.join('|') + ')';
|
||||
}
|
||||
else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a map from rule name to match text.
|
||||
*
|
||||
* @param yaml The root of the YAML document.
|
||||
* @returns A map whose keys are the names of rules, and whose values are the corresponding match
|
||||
* text of each rule.
|
||||
*/
|
||||
function gatherMatchTextForRules(yaml: any): Map<string, string> {
|
||||
const replacements = new Map<string, string>();
|
||||
for (const key in yaml.repository) {
|
||||
const node = yaml.repository[key];
|
||||
replacements.set(key, getNodeMatchText(node));
|
||||
}
|
||||
|
||||
return replacements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the specified callback function on each rule definition in the file.
|
||||
*
|
||||
* @param yaml The root of the YAML document.
|
||||
* @param action Callback to invoke on each rule.
|
||||
*/
|
||||
function visitAllRulesInFile(yaml: any, action: (rule: any) => void) {
|
||||
visitAllRulesInRuleMap(yaml.patterns, action);
|
||||
visitAllRulesInRuleMap(yaml.repository, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the specified callback function on each rule definition in a map or array of rules.
|
||||
* For rules that have a `patterns` element defined child rules, the children are included in the
|
||||
* visitation.
|
||||
*
|
||||
* @param ruleMap The map or array of rules to visit.
|
||||
* @param action Callback to invoke on each rule.
|
||||
*/
|
||||
function visitAllRulesInRuleMap(ruleMap: any, action: (rule: any) => void) {
|
||||
for (const key in ruleMap) {
|
||||
const rule = ruleMap[key];
|
||||
if ((typeof rule) === 'object') {
|
||||
action(rule);
|
||||
if (rule.patterns !== undefined) {
|
||||
visitAllRulesInRuleMap(rule.patterns, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the specified transformation on all match patterns in the specified rule.
|
||||
*
|
||||
* @param rule The rule whose matches are to be transformed.
|
||||
* @param action The transformation to make on each match pattern.
|
||||
*/
|
||||
function visitAllMatchesInRule(rule: any, action: (match: any) => any) {
|
||||
for (const key in rule) {
|
||||
switch (key) {
|
||||
case 'begin':
|
||||
case 'end':
|
||||
case 'match':
|
||||
case 'while':
|
||||
rule[key] = action(rule[key]);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace any usage of the specified `beginPattern` or `endPattern` property with the equivalent
|
||||
* `begin`/`beginCaptures` or `end`/`endCaptures` properties.
|
||||
*
|
||||
* @param rule Rule to be transformed.
|
||||
* @param key Base key of the property to be transformed.
|
||||
*/
|
||||
function expandPatternMatchProperties(rule: any, key: 'begin' | 'end') {
|
||||
const patternKey = key + 'Pattern';
|
||||
const capturesKey = key + 'Captures';
|
||||
const pattern = rule[patternKey];
|
||||
if (pattern !== undefined) {
|
||||
const patterns: string[] = Array.isArray(pattern) ? pattern : [pattern];
|
||||
rule[key] = patterns.map(p => `((?${p}))`).join('|');
|
||||
const captures: { [index: string]: any } = {};
|
||||
for (const patternIndex in patterns) {
|
||||
captures[(Number(patternIndex) + 1).toString()] = {
|
||||
patterns: [
|
||||
{
|
||||
include: patterns[patternIndex]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
rule[capturesKey] = captures;
|
||||
rule[patternKey] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the specified document to produce a TextMate grammar.
|
||||
*
|
||||
* @param yaml The root of the YAML document.
|
||||
*/
|
||||
function transformFile(yaml: any) {
|
||||
const macros = gatherMacros(yaml);
|
||||
visitAllRulesInFile(yaml, (rule) => {
|
||||
expandPatternMatchProperties(rule, 'begin');
|
||||
expandPatternMatchProperties(rule, 'end');
|
||||
});
|
||||
|
||||
// Expand macros in matches.
|
||||
visitAllRulesInFile(yaml, (rule) => {
|
||||
visitAllMatchesInRule(rule, (match) => {
|
||||
if ((typeof match) === 'object') {
|
||||
for (const key in match) {
|
||||
return macros.get(key)!.replace('(?#)', `(?:${match[key]})`);
|
||||
}
|
||||
throw new Error('No key in macro map.');
|
||||
}
|
||||
else {
|
||||
return match;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
yaml.macros = undefined;
|
||||
|
||||
const replacements = gatherMatchTextForRules(yaml);
|
||||
// Expand references in matches.
|
||||
visitAllRulesInFile(yaml, (rule) => {
|
||||
visitAllMatchesInRule(rule, (match) => {
|
||||
return replaceReferencesWithStrings(match, replacements);
|
||||
});
|
||||
});
|
||||
|
||||
if (yaml.regexOptions !== undefined) {
|
||||
const regexOptions = '(?' + yaml.regexOptions + ')';
|
||||
visitAllRulesInFile(yaml, (rule) => {
|
||||
visitAllMatchesInRule(rule, (match) => {
|
||||
return regexOptions + match;
|
||||
});
|
||||
});
|
||||
|
||||
yaml.regexOptions = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function transpileTextMateGrammar() {
|
||||
return through.obj((file: Vinyl, _encoding: string, callback: Function): void => {
|
||||
if (file.isNull()) {
|
||||
callback(null, file);
|
||||
}
|
||||
else if (file.isBuffer()) {
|
||||
const buf: Buffer = file.contents;
|
||||
const yamlText: string = buf.toString('utf8');
|
||||
const jsonData: any = jsYaml.safeLoad(yamlText);
|
||||
transformFile(jsonData);
|
||||
|
||||
file.contents = Buffer.from(JSON.stringify(jsonData, null, 2), 'utf8');
|
||||
file.extname = '.json';
|
||||
callback(null, file);
|
||||
}
|
||||
else {
|
||||
callback('error', new PluginError('transpileTextMateGrammar', 'Format not supported.'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function compileTextMateGrammar() {
|
||||
return gulp.src('syntaxes/*.tmLanguage.yml')
|
||||
.pipe(transpileTextMateGrammar())
|
||||
.pipe(gulp.dest('out/syntaxes'));
|
||||
}
|
||||
22
extensions/ql-vscode/_gulpfile.ts/tsconfig.json
Normal file
22
extensions/ql-vscode/_gulpfile.ts/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
"module": "commonjs",
|
||||
"target": "es2017",
|
||||
"lib": ["es6"],
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"strictNullChecks": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"preserveWatchOutput": true,
|
||||
"newLine": "lf",
|
||||
"noImplicitReturns": true,
|
||||
"experimentalDecorators": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
},
|
||||
"include": ["*.ts"]
|
||||
}
|
||||
50
extensions/ql-vscode/_gulpfile.ts/typescript.ts
Normal file
50
extensions/ql-vscode/_gulpfile.ts/typescript.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import * as colors from 'ansi-colors';
|
||||
import * as gulp from 'gulp';
|
||||
import * as path from 'path';
|
||||
import * as sourcemaps from 'gulp-sourcemaps';
|
||||
import * as ts from 'gulp-typescript';
|
||||
|
||||
function goodReporter(): ts.reporter.Reporter {
|
||||
return {
|
||||
error: (error, typescript) => {
|
||||
if (error.tsFile) {
|
||||
console.log('[' + colors.gray('gulp-typescript') + '] ' + colors.red(error.fullFilename
|
||||
+ '(' + (error.startPosition!.line + 1) + ',' + error.startPosition!.character + '): ')
|
||||
+ 'error TS' + error.diagnostic.code + ': ' + typescript.flattenDiagnosticMessageText(error.diagnostic.messageText, '\n'));
|
||||
}
|
||||
else {
|
||||
console.log(error.message);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const tsProject = ts.createProject('tsconfig.json');
|
||||
|
||||
export function compileTypeScript() {
|
||||
return tsProject.src()
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(tsProject(goodReporter()))
|
||||
.pipe((sourcemaps as any).mapSources((sourcePath: string, _file: string) => {
|
||||
// The source path is kind of odd, because it's relative to the `tsconfig.json` file in the
|
||||
// `typescript-config` package, which lives in the `node_modules` directory of the package
|
||||
// that is being built. It starts out as something like '../../../src/foo.ts', and we need to
|
||||
// strip out the leading '../../../'.
|
||||
return path.join('a/b/c', sourcePath);
|
||||
}))
|
||||
.pipe(sourcemaps.write('.', {
|
||||
includeContent: false,
|
||||
sourceRoot: '.', // XXX this is probably wrong
|
||||
}))
|
||||
.pipe(gulp.dest('out'));
|
||||
}
|
||||
|
||||
export function watchTypeScript() {
|
||||
gulp.watch('src/**/*.ts', compileTypeScript);
|
||||
}
|
||||
|
||||
/** Copy CSS files for the results view into the output directory. */
|
||||
export function copyViewCss() {
|
||||
return gulp.src('src/view/*.css')
|
||||
.pipe(gulp.dest('out'));
|
||||
}
|
||||
65
extensions/ql-vscode/_gulpfile.ts/webpack.config.ts
Normal file
65
extensions/ql-vscode/_gulpfile.ts/webpack.config.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import * as path from 'path';
|
||||
import * as webpack from 'webpack';
|
||||
|
||||
export const config: webpack.Configuration = {
|
||||
mode: 'development',
|
||||
entry: {
|
||||
resultsView: './src/view/results.tsx',
|
||||
compareView: './src/compare/view/Compare.tsx',
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, '..', 'out'),
|
||||
filename: '[name].js'
|
||||
},
|
||||
devtool: 'inline-source-map',
|
||||
resolve: {
|
||||
extensions: ['.js', '.ts', '.tsx', '.json']
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
configFile: 'src/view/tsconfig.json',
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.less$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'style-loader'
|
||||
},
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
sourceMap: true
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'less-loader',
|
||||
options: {
|
||||
javascriptEnabled: true,
|
||||
sourceMap: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'style-loader'
|
||||
},
|
||||
{
|
||||
loader: 'css-loader'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
performance: {
|
||||
hints: false
|
||||
}
|
||||
};
|
||||
28
extensions/ql-vscode/_gulpfile.ts/webpack.ts
Normal file
28
extensions/ql-vscode/_gulpfile.ts/webpack.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import * as webpack from 'webpack';
|
||||
import { config } from './webpack.config';
|
||||
|
||||
export function compileView(cb: (err?: Error) => void) {
|
||||
webpack(config).run((error, stats) => {
|
||||
if (error) {
|
||||
cb(error);
|
||||
}
|
||||
console.log(stats.toString({
|
||||
errorDetails: true,
|
||||
colors: true,
|
||||
assets: false,
|
||||
builtAt: false,
|
||||
version: false,
|
||||
hash: false,
|
||||
entrypoints: false,
|
||||
timings: false,
|
||||
modules: false,
|
||||
errors: true
|
||||
}));
|
||||
if (stats.hasErrors()) {
|
||||
cb(new Error('Compilation errors detected.'));
|
||||
return;
|
||||
}
|
||||
|
||||
cb();
|
||||
});
|
||||
}
|
||||
10023
extensions/ql-vscode/package-lock.json
generated
Normal file
10023
extensions/ql-vscode/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -586,90 +586,91 @@
|
||||
"preintegration": "rm -rf ./out/vscode-tests && gulp",
|
||||
"integration": "node ./out/vscode-tests/run-integration-tests.js",
|
||||
"update-vscode": "node ./node_modules/vscode/bin/install",
|
||||
"postinstall": "npm rebuild && node ./node_modules/vscode/bin/install",
|
||||
"format": "tsfmt -r && eslint src test --ext .ts,.tsx --fix",
|
||||
"lint": "eslint src test --ext .ts,.tsx --max-warnings=0",
|
||||
"format-staged": "lint-staged"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/semver": "~7.2.0",
|
||||
"child-process-promise": "^2.2.1",
|
||||
"classnames": "~2.2.6",
|
||||
"fs-extra": "^8.1.0",
|
||||
"glob-promise": "^3.4.0",
|
||||
"js-yaml": "^3.12.0",
|
||||
"js-yaml": "^3.14.0",
|
||||
"minimist": "~1.2.5",
|
||||
"node-fetch": "~2.6.0",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"semver": "~7.3.2",
|
||||
"tmp": "^0.1.0",
|
||||
"tmp-promise": "~3.0.2",
|
||||
"tree-kill": "~1.2.2",
|
||||
"unzipper": "~0.10.5",
|
||||
"vscode-jsonrpc": "^5.0.1",
|
||||
"vscode-languageclient": "^6.1.3",
|
||||
"vscode-test-adapter-api": "~1.7.0",
|
||||
"vscode-test-adapter-util": "~0.7.0",
|
||||
"minimist": "~1.2.5",
|
||||
"semver": "~7.3.2",
|
||||
"@types/semver": "~7.2.0",
|
||||
"tmp-promise": "~3.0.2",
|
||||
"zip-a-folder": "~0.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.1.7",
|
||||
"@types/chai-as-promised": "~7.1.2",
|
||||
"@types/child-process-promise": "^2.2.1",
|
||||
"@types/classnames": "~2.2.9",
|
||||
"@types/fs-extra": "^8.0.0",
|
||||
"@types/glob": "^7.1.1",
|
||||
"@types/google-protobuf": "^3.2.7",
|
||||
"@types/gulp": "^4.0.6",
|
||||
"@types/js-yaml": "~3.12.1",
|
||||
"@types/gulp-sourcemaps": "0.0.32",
|
||||
"@types/js-yaml": "=3.12.2",
|
||||
"@types/jszip": "~3.1.6",
|
||||
"@types/mocha": "~5.2.7",
|
||||
"@types/node": "^12.0.8",
|
||||
"@types/node-fetch": "~2.5.2",
|
||||
"@types/proxyquire": "~1.3.28",
|
||||
"@types/react": "^16.8.17",
|
||||
"@types/react-dom": "^16.8.4",
|
||||
"@types/sarif": "~2.1.2",
|
||||
"@types/sinon": "~7.5.2",
|
||||
"@types/sinon-chai": "~3.2.3",
|
||||
"@types/through2": "^2.0.36",
|
||||
"@types/tmp": "^0.1.0",
|
||||
"@types/unzipper": "~0.10.1",
|
||||
"@types/vscode": "^1.39.0",
|
||||
"@types/vscode": "=1.43.0",
|
||||
"@types/webpack": "^4.32.1",
|
||||
"@types/xml2js": "~0.4.4",
|
||||
"@github/codeql-gulp-tasks": "^0.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "~2.23.0",
|
||||
"@typescript-eslint/parser": "~2.23.0",
|
||||
"ansi-colors": "^4.1.1",
|
||||
"chai": "^4.2.0",
|
||||
"chai-as-promised": "~7.1.1",
|
||||
"css-loader": "~3.1.0",
|
||||
"eslint": "~6.8.0",
|
||||
"eslint-plugin-react": "~7.19.0",
|
||||
"glob": "^7.1.4",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-sourcemaps": "^2.6.5",
|
||||
"gulp-typescript": "^5.0.1",
|
||||
"husky": "~4.2.5",
|
||||
"lint-staged": "~10.2.2",
|
||||
"mocha": "~6.2.1",
|
||||
"mocha-sinon": "~2.1.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "~2.0.5",
|
||||
"proxyquire": "~2.1.3",
|
||||
"sinon": "~9.0.0",
|
||||
"sinon-chai": "~3.5.0",
|
||||
"style-loader": "~0.23.1",
|
||||
"through2": "^3.0.1",
|
||||
"ts-loader": "^5.4.5",
|
||||
"ts-node": "^8.3.0",
|
||||
"ts-protoc-gen": "^0.9.0",
|
||||
"typescript": "^3.7.2",
|
||||
"typescript": "=3.8.3",
|
||||
"typescript-formatter": "^7.2.2",
|
||||
"vsce": "^1.65.0",
|
||||
"vscode-test": "^1.4.0",
|
||||
"webpack": "^4.38.0",
|
||||
"webpack-cli": "^3.3.2",
|
||||
"eslint": "~6.8.0",
|
||||
"@typescript-eslint/eslint-plugin": "~2.23.0",
|
||||
"@typescript-eslint/parser": "~2.23.0",
|
||||
"chai-as-promised": "~7.1.1",
|
||||
"@types/chai-as-promised": "~7.1.2",
|
||||
"@types/sinon": "~7.5.2",
|
||||
"sinon-chai": "~3.5.0",
|
||||
"@types/sinon-chai": "~3.2.3",
|
||||
"proxyquire": "~2.1.3",
|
||||
"@types/proxyquire": "~1.3.28",
|
||||
"eslint-plugin-react": "~7.19.0",
|
||||
"husky": "~4.2.5",
|
||||
"lint-staged": "~10.2.2",
|
||||
"prettier": "~2.0.5"
|
||||
"webpack-cli": "^3.3.2"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Disposable } from "vscode";
|
||||
import { Disposable } from 'vscode';
|
||||
|
||||
/**
|
||||
* Base class to make it easier to implement a `Disposable` that owns other disposable object.
|
||||
@@ -7,9 +7,6 @@ export abstract class DisposableObject implements Disposable {
|
||||
private disposables: Disposable[] = [];
|
||||
private tracked?: Set<Disposable> = undefined;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds `obj` to a list of objects to dispose when `this` is disposed. Objects added by `push` are
|
||||
* disposed in reverse order of being added.
|
||||
|
||||
Reference in New Issue
Block a user