mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Merge from vscode a234f13c45b40a0929777cb440ee011b7549eed2 (#8911)
* Merge from vscode a234f13c45b40a0929777cb440ee011b7549eed2 * update distro * fix layering * update distro * fix tests
This commit is contained in:
59
build/lib/eslint/code-import-patterns.js
Normal file
59
build/lib/eslint/code-import-patterns.js
Normal file
@@ -0,0 +1,59 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
const path_1 = require("path");
|
||||
const minimatch = require("minimatch");
|
||||
const utils_1 = require("./utils");
|
||||
module.exports = new class {
|
||||
constructor() {
|
||||
this.meta = {
|
||||
messages: {
|
||||
badImport: 'Imports violates \'{{restrictions}}\' restrictions. See https://github.com/microsoft/vscode/wiki/Source-Code-Organization'
|
||||
},
|
||||
docs: {
|
||||
url: 'https://github.com/microsoft/vscode/wiki/Source-Code-Organization'
|
||||
}
|
||||
};
|
||||
}
|
||||
create(context) {
|
||||
const configs = context.options;
|
||||
for (const config of configs) {
|
||||
if (minimatch(context.getFilename(), config.target)) {
|
||||
return utils_1.createImportRuleListener((node, value) => this._checkImport(context, config, node, value));
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
_checkImport(context, config, node, path) {
|
||||
// resolve relative paths
|
||||
if (path[0] === '.') {
|
||||
path = path_1.join(context.getFilename(), path);
|
||||
}
|
||||
let restrictions;
|
||||
if (typeof config.restrictions === 'string') {
|
||||
restrictions = [config.restrictions];
|
||||
}
|
||||
else {
|
||||
restrictions = config.restrictions;
|
||||
}
|
||||
let matched = false;
|
||||
for (const pattern of restrictions) {
|
||||
if (minimatch(path, pattern)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
// None of the restrictions matched
|
||||
context.report({
|
||||
loc: node.loc,
|
||||
messageId: 'badImport',
|
||||
data: {
|
||||
restrictions: restrictions.join(' or ')
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
75
build/lib/eslint/code-import-patterns.ts
Normal file
75
build/lib/eslint/code-import-patterns.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as eslint from 'eslint';
|
||||
import { TSESTree } from '@typescript-eslint/experimental-utils';
|
||||
import { join } from 'path';
|
||||
import * as minimatch from 'minimatch';
|
||||
import { createImportRuleListener } from './utils';
|
||||
|
||||
interface ImportPatternsConfig {
|
||||
target: string;
|
||||
restrictions: string | string[];
|
||||
}
|
||||
|
||||
export = new class implements eslint.Rule.RuleModule {
|
||||
|
||||
readonly meta: eslint.Rule.RuleMetaData = {
|
||||
messages: {
|
||||
badImport: 'Imports violates \'{{restrictions}}\' restrictions. See https://github.com/microsoft/vscode/wiki/Source-Code-Organization'
|
||||
},
|
||||
docs: {
|
||||
url: 'https://github.com/microsoft/vscode/wiki/Source-Code-Organization'
|
||||
}
|
||||
};
|
||||
|
||||
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
|
||||
|
||||
const configs = <ImportPatternsConfig[]>context.options;
|
||||
|
||||
for (const config of configs) {
|
||||
if (minimatch(context.getFilename(), config.target)) {
|
||||
return createImportRuleListener((node, value) => this._checkImport(context, config, node, value));
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
private _checkImport(context: eslint.Rule.RuleContext, config: ImportPatternsConfig, node: TSESTree.Node, path: string) {
|
||||
|
||||
// resolve relative paths
|
||||
if (path[0] === '.') {
|
||||
path = join(context.getFilename(), path);
|
||||
}
|
||||
|
||||
let restrictions: string[];
|
||||
if (typeof config.restrictions === 'string') {
|
||||
restrictions = [config.restrictions];
|
||||
} else {
|
||||
restrictions = config.restrictions;
|
||||
}
|
||||
|
||||
let matched = false;
|
||||
for (const pattern of restrictions) {
|
||||
if (minimatch(path, pattern)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
// None of the restrictions matched
|
||||
context.report({
|
||||
loc: node.loc,
|
||||
messageId: 'badImport',
|
||||
data: {
|
||||
restrictions: restrictions.join(' or ')
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
68
build/lib/eslint/code-layering.js
Normal file
68
build/lib/eslint/code-layering.js
Normal file
@@ -0,0 +1,68 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
const path_1 = require("path");
|
||||
const utils_1 = require("./utils");
|
||||
module.exports = new class {
|
||||
constructor() {
|
||||
this.meta = {
|
||||
messages: {
|
||||
layerbreaker: 'Bad layering. You are not allowed to access {{from}} from here, allowed layers are: [{{allowed}}]'
|
||||
},
|
||||
docs: {
|
||||
url: 'https://github.com/microsoft/vscode/wiki/Source-Code-Organization'
|
||||
}
|
||||
};
|
||||
}
|
||||
create(context) {
|
||||
const fileDirname = path_1.dirname(context.getFilename());
|
||||
const parts = fileDirname.split(/\\|\//);
|
||||
const ruleArgs = context.options[0];
|
||||
let config;
|
||||
for (let i = parts.length - 1; i >= 0; i--) {
|
||||
if (ruleArgs[parts[i]]) {
|
||||
config = {
|
||||
allowed: new Set(ruleArgs[parts[i]]).add(parts[i]),
|
||||
disallowed: new Set()
|
||||
};
|
||||
Object.keys(ruleArgs).forEach(key => {
|
||||
if (!config.allowed.has(key)) {
|
||||
config.disallowed.add(key);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!config) {
|
||||
// nothing
|
||||
return {};
|
||||
}
|
||||
return utils_1.createImportRuleListener((node, path) => {
|
||||
if (path[0] === '.') {
|
||||
path = path_1.join(path_1.dirname(context.getFilename()), path);
|
||||
}
|
||||
const parts = path_1.dirname(path).split(/\\|\//);
|
||||
for (let i = parts.length - 1; i >= 0; i--) {
|
||||
const part = parts[i];
|
||||
if (config.allowed.has(part)) {
|
||||
// GOOD - same layer
|
||||
break;
|
||||
}
|
||||
if (config.disallowed.has(part)) {
|
||||
// BAD - wrong layer
|
||||
context.report({
|
||||
loc: node.loc,
|
||||
messageId: 'layerbreaker',
|
||||
data: {
|
||||
from: part,
|
||||
allowed: [...config.allowed.keys()].join(', ')
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
83
build/lib/eslint/code-layering.ts
Normal file
83
build/lib/eslint/code-layering.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as eslint from 'eslint';
|
||||
import { join, dirname } from 'path';
|
||||
import { createImportRuleListener } from './utils';
|
||||
|
||||
type Config = {
|
||||
allowed: Set<string>;
|
||||
disallowed: Set<string>;
|
||||
};
|
||||
|
||||
export = new class implements eslint.Rule.RuleModule {
|
||||
|
||||
readonly meta: eslint.Rule.RuleMetaData = {
|
||||
messages: {
|
||||
layerbreaker: 'Bad layering. You are not allowed to access {{from}} from here, allowed layers are: [{{allowed}}]'
|
||||
},
|
||||
docs: {
|
||||
url: 'https://github.com/microsoft/vscode/wiki/Source-Code-Organization'
|
||||
}
|
||||
};
|
||||
|
||||
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
|
||||
|
||||
const fileDirname = dirname(context.getFilename());
|
||||
const parts = fileDirname.split(/\\|\//);
|
||||
const ruleArgs = <Record<string, string[]>>context.options[0];
|
||||
|
||||
let config: Config | undefined;
|
||||
for (let i = parts.length - 1; i >= 0; i--) {
|
||||
if (ruleArgs[parts[i]]) {
|
||||
config = {
|
||||
allowed: new Set(ruleArgs[parts[i]]).add(parts[i]),
|
||||
disallowed: new Set()
|
||||
};
|
||||
Object.keys(ruleArgs).forEach(key => {
|
||||
if (!config!.allowed.has(key)) {
|
||||
config!.disallowed.add(key);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!config) {
|
||||
// nothing
|
||||
return {};
|
||||
}
|
||||
|
||||
return createImportRuleListener((node, path) => {
|
||||
if (path[0] === '.') {
|
||||
path = join(dirname(context.getFilename()), path);
|
||||
}
|
||||
|
||||
const parts = dirname(path).split(/\\|\//);
|
||||
for (let i = parts.length - 1; i >= 0; i--) {
|
||||
const part = parts[i];
|
||||
|
||||
if (config!.allowed.has(part)) {
|
||||
// GOOD - same layer
|
||||
break;
|
||||
}
|
||||
|
||||
if (config!.disallowed.has(part)) {
|
||||
// BAD - wrong layer
|
||||
context.report({
|
||||
loc: node.loc,
|
||||
messageId: 'layerbreaker',
|
||||
data: {
|
||||
from: part,
|
||||
allowed: [...config!.allowed.keys()].join(', ')
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
38
build/lib/eslint/code-no-nls-in-standalone-editor.js
Normal file
38
build/lib/eslint/code-no-nls-in-standalone-editor.js
Normal file
@@ -0,0 +1,38 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
const path_1 = require("path");
|
||||
const utils_1 = require("./utils");
|
||||
module.exports = new class NoNlsInStandaloneEditorRule {
|
||||
constructor() {
|
||||
this.meta = {
|
||||
messages: {
|
||||
noNls: 'Not allowed to import vs/nls in standalone editor modules. Use standaloneStrings.ts'
|
||||
}
|
||||
};
|
||||
}
|
||||
create(context) {
|
||||
const fileName = context.getFilename();
|
||||
if (/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(fileName)) {
|
||||
return utils_1.createImportRuleListener((node, path) => {
|
||||
// resolve relative paths
|
||||
if (path[0] === '.') {
|
||||
path = path_1.join(context.getFilename(), path);
|
||||
}
|
||||
if (/vs(\/|\\)nls/.test(path)) {
|
||||
context.report({
|
||||
loc: node.loc,
|
||||
messageId: 'noNls'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return {};
|
||||
}
|
||||
};
|
||||
48
build/lib/eslint/code-no-nls-in-standalone-editor.ts
Normal file
48
build/lib/eslint/code-no-nls-in-standalone-editor.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as eslint from 'eslint';
|
||||
import { join } from 'path';
|
||||
import { createImportRuleListener } from './utils';
|
||||
|
||||
export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule {
|
||||
|
||||
readonly meta: eslint.Rule.RuleMetaData = {
|
||||
messages: {
|
||||
noNls: 'Not allowed to import vs/nls in standalone editor modules. Use standaloneStrings.ts'
|
||||
}
|
||||
};
|
||||
|
||||
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
|
||||
|
||||
const fileName = context.getFilename();
|
||||
if (
|
||||
/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(fileName)
|
||||
) {
|
||||
return createImportRuleListener((node, path) => {
|
||||
// resolve relative paths
|
||||
if (path[0] === '.') {
|
||||
path = join(context.getFilename(), path);
|
||||
}
|
||||
|
||||
if (
|
||||
/vs(\/|\\)nls/.test(path)
|
||||
) {
|
||||
context.report({
|
||||
loc: node.loc,
|
||||
messageId: 'noNls'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
41
build/lib/eslint/code-no-standalone-editor.js
Normal file
41
build/lib/eslint/code-no-standalone-editor.js
Normal file
@@ -0,0 +1,41 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
const path_1 = require("path");
|
||||
const utils_1 = require("./utils");
|
||||
module.exports = new class NoNlsInStandaloneEditorRule {
|
||||
constructor() {
|
||||
this.meta = {
|
||||
messages: {
|
||||
badImport: 'Not allowed to import standalone editor modules.'
|
||||
},
|
||||
docs: {
|
||||
url: 'https://github.com/microsoft/vscode/wiki/Source-Code-Organization'
|
||||
}
|
||||
};
|
||||
}
|
||||
create(context) {
|
||||
if (/vs(\/|\\)editor/.test(context.getFilename())) {
|
||||
// the vs/editor folder is allowed to use the standalone editor
|
||||
return {};
|
||||
}
|
||||
return utils_1.createImportRuleListener((node, path) => {
|
||||
// resolve relative paths
|
||||
if (path[0] === '.') {
|
||||
path = path_1.join(context.getFilename(), path);
|
||||
}
|
||||
if (/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(path)) {
|
||||
context.report({
|
||||
loc: node.loc,
|
||||
messageId: 'badImport'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
50
build/lib/eslint/code-no-standalone-editor.ts
Normal file
50
build/lib/eslint/code-no-standalone-editor.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as eslint from 'eslint';
|
||||
import { join } from 'path';
|
||||
import { createImportRuleListener } from './utils';
|
||||
|
||||
export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule {
|
||||
|
||||
readonly meta: eslint.Rule.RuleMetaData = {
|
||||
messages: {
|
||||
badImport: 'Not allowed to import standalone editor modules.'
|
||||
},
|
||||
docs: {
|
||||
url: 'https://github.com/microsoft/vscode/wiki/Source-Code-Organization'
|
||||
}
|
||||
};
|
||||
|
||||
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
|
||||
|
||||
if (/vs(\/|\\)editor/.test(context.getFilename())) {
|
||||
// the vs/editor folder is allowed to use the standalone editor
|
||||
return {};
|
||||
}
|
||||
|
||||
return createImportRuleListener((node, path) => {
|
||||
|
||||
// resolve relative paths
|
||||
if (path[0] === '.') {
|
||||
path = join(context.getFilename(), path);
|
||||
}
|
||||
|
||||
if (
|
||||
/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(path)
|
||||
) {
|
||||
context.report({
|
||||
loc: node.loc,
|
||||
messageId: 'badImport'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
111
build/lib/eslint/code-no-unexternalized-strings.js
Normal file
111
build/lib/eslint/code-no-unexternalized-strings.js
Normal file
@@ -0,0 +1,111 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var _a;
|
||||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils");
|
||||
function isStringLiteral(node) {
|
||||
return !!node && node.type === experimental_utils_1.AST_NODE_TYPES.Literal && typeof node.value === 'string';
|
||||
}
|
||||
function isDoubleQuoted(node) {
|
||||
return node.raw[0] === '"' && node.raw[node.raw.length - 1] === '"';
|
||||
}
|
||||
module.exports = new (_a = class NoUnexternalizedStrings {
|
||||
constructor() {
|
||||
this.meta = {
|
||||
messages: {
|
||||
doubleQuoted: 'Only use double-quoted strings for externalized strings.',
|
||||
badKey: 'The key \'{{key}}\' doesn\'t conform to a valid localize identifier.',
|
||||
duplicateKey: 'Duplicate key \'{{key}}\' with different message value.',
|
||||
badMessage: 'Message argument to \'{{message}}\' must be a string literal.'
|
||||
}
|
||||
};
|
||||
}
|
||||
create(context) {
|
||||
const externalizedStringLiterals = new Map();
|
||||
const doubleQuotedStringLiterals = new Set();
|
||||
function collectDoubleQuotedStrings(node) {
|
||||
if (isStringLiteral(node) && isDoubleQuoted(node)) {
|
||||
doubleQuotedStringLiterals.add(node);
|
||||
}
|
||||
}
|
||||
function visitLocalizeCall(node) {
|
||||
// localize(key, message)
|
||||
const [keyNode, messageNode] = node.arguments;
|
||||
// (1)
|
||||
// extract key so that it can be checked later
|
||||
let key;
|
||||
if (isStringLiteral(keyNode)) {
|
||||
doubleQuotedStringLiterals.delete(keyNode); //todo@joh reconsider
|
||||
key = keyNode.value;
|
||||
}
|
||||
else if (keyNode.type === experimental_utils_1.AST_NODE_TYPES.ObjectExpression) {
|
||||
for (let property of keyNode.properties) {
|
||||
if (property.type === experimental_utils_1.AST_NODE_TYPES.Property && !property.computed) {
|
||||
if (property.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier && property.key.name === 'key') {
|
||||
if (isStringLiteral(property.value)) {
|
||||
doubleQuotedStringLiterals.delete(property.value); //todo@joh reconsider
|
||||
key = property.value.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeof key === 'string') {
|
||||
let array = externalizedStringLiterals.get(key);
|
||||
if (!array) {
|
||||
array = [];
|
||||
externalizedStringLiterals.set(key, array);
|
||||
}
|
||||
array.push({ call: node, message: messageNode });
|
||||
}
|
||||
// (2)
|
||||
// remove message-argument from doubleQuoted list and make
|
||||
// sure it is a string-literal
|
||||
doubleQuotedStringLiterals.delete(messageNode);
|
||||
if (!isStringLiteral(messageNode)) {
|
||||
context.report({
|
||||
loc: messageNode.loc,
|
||||
messageId: 'badMessage',
|
||||
data: { message: context.getSourceCode().getText(node) }
|
||||
});
|
||||
}
|
||||
}
|
||||
function reportBadStringsAndBadKeys() {
|
||||
// (1)
|
||||
// report all strings that are in double quotes
|
||||
for (const node of doubleQuotedStringLiterals) {
|
||||
context.report({ loc: node.loc, messageId: 'doubleQuoted' });
|
||||
}
|
||||
for (const [key, values] of externalizedStringLiterals) {
|
||||
// (2)
|
||||
// report all invalid NLS keys
|
||||
if (!key.match(NoUnexternalizedStrings._rNlsKeys)) {
|
||||
for (let value of values) {
|
||||
context.report({ loc: value.call.loc, messageId: 'badKey', data: { key } });
|
||||
}
|
||||
}
|
||||
// (2)
|
||||
// report all invalid duplicates (same key, different message)
|
||||
if (values.length > 1) {
|
||||
for (let i = 1; i < values.length; i++) {
|
||||
if (context.getSourceCode().getText(values[i - 1].message) !== context.getSourceCode().getText(values[i].message)) {
|
||||
context.report({ loc: values[i].call.loc, messageId: 'duplicateKey', data: { key } });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
['Literal']: (node) => collectDoubleQuotedStrings(node),
|
||||
['ExpressionStatement[directive] Literal:exit']: (node) => doubleQuotedStringLiterals.delete(node),
|
||||
['CallExpression[callee.type="MemberExpression"][callee.object.name="nls"][callee.property.name="localize"]:exit']: (node) => visitLocalizeCall(node),
|
||||
['CallExpression[callee.name="localize"][arguments.length>=2]:exit']: (node) => visitLocalizeCall(node),
|
||||
['Program:exit']: reportBadStringsAndBadKeys,
|
||||
};
|
||||
}
|
||||
},
|
||||
_a._rNlsKeys = /^[_a-zA-Z0-9][ .\-_a-zA-Z0-9]*$/,
|
||||
_a);
|
||||
126
build/lib/eslint/code-no-unexternalized-strings.ts
Normal file
126
build/lib/eslint/code-no-unexternalized-strings.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as eslint from 'eslint';
|
||||
import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/experimental-utils';
|
||||
|
||||
function isStringLiteral(node: TSESTree.Node | null | undefined): node is TSESTree.StringLiteral {
|
||||
return !!node && node.type === AST_NODE_TYPES.Literal && typeof node.value === 'string';
|
||||
}
|
||||
|
||||
function isDoubleQuoted(node: TSESTree.StringLiteral): boolean {
|
||||
return node.raw[0] === '"' && node.raw[node.raw.length - 1] === '"';
|
||||
}
|
||||
|
||||
export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule {
|
||||
|
||||
private static _rNlsKeys = /^[_a-zA-Z0-9][ .\-_a-zA-Z0-9]*$/;
|
||||
|
||||
readonly meta: eslint.Rule.RuleMetaData = {
|
||||
messages: {
|
||||
doubleQuoted: 'Only use double-quoted strings for externalized strings.',
|
||||
badKey: 'The key \'{{key}}\' doesn\'t conform to a valid localize identifier.',
|
||||
duplicateKey: 'Duplicate key \'{{key}}\' with different message value.',
|
||||
badMessage: 'Message argument to \'{{message}}\' must be a string literal.'
|
||||
}
|
||||
};
|
||||
|
||||
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
|
||||
|
||||
const externalizedStringLiterals = new Map<string, { call: TSESTree.CallExpression, message: TSESTree.Node }[]>();
|
||||
const doubleQuotedStringLiterals = new Set<TSESTree.Node>();
|
||||
|
||||
function collectDoubleQuotedStrings(node: TSESTree.Literal) {
|
||||
if (isStringLiteral(node) && isDoubleQuoted(node)) {
|
||||
doubleQuotedStringLiterals.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
function visitLocalizeCall(node: TSESTree.CallExpression) {
|
||||
|
||||
// localize(key, message)
|
||||
const [keyNode, messageNode] = (<TSESTree.CallExpression>node).arguments;
|
||||
|
||||
// (1)
|
||||
// extract key so that it can be checked later
|
||||
let key: string | undefined;
|
||||
if (isStringLiteral(keyNode)) {
|
||||
doubleQuotedStringLiterals.delete(keyNode); //todo@joh reconsider
|
||||
key = keyNode.value;
|
||||
|
||||
} else if (keyNode.type === AST_NODE_TYPES.ObjectExpression) {
|
||||
for (let property of keyNode.properties) {
|
||||
if (property.type === AST_NODE_TYPES.Property && !property.computed) {
|
||||
if (property.key.type === AST_NODE_TYPES.Identifier && property.key.name === 'key') {
|
||||
if (isStringLiteral(property.value)) {
|
||||
doubleQuotedStringLiterals.delete(property.value); //todo@joh reconsider
|
||||
key = property.value.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeof key === 'string') {
|
||||
let array = externalizedStringLiterals.get(key);
|
||||
if (!array) {
|
||||
array = [];
|
||||
externalizedStringLiterals.set(key, array);
|
||||
}
|
||||
array.push({ call: node, message: messageNode });
|
||||
}
|
||||
|
||||
// (2)
|
||||
// remove message-argument from doubleQuoted list and make
|
||||
// sure it is a string-literal
|
||||
doubleQuotedStringLiterals.delete(messageNode);
|
||||
if (!isStringLiteral(messageNode)) {
|
||||
context.report({
|
||||
loc: messageNode.loc,
|
||||
messageId: 'badMessage',
|
||||
data: { message: context.getSourceCode().getText(<any>node) }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function reportBadStringsAndBadKeys() {
|
||||
// (1)
|
||||
// report all strings that are in double quotes
|
||||
for (const node of doubleQuotedStringLiterals) {
|
||||
context.report({ loc: node.loc, messageId: 'doubleQuoted' });
|
||||
}
|
||||
|
||||
for (const [key, values] of externalizedStringLiterals) {
|
||||
|
||||
// (2)
|
||||
// report all invalid NLS keys
|
||||
if (!key.match(NoUnexternalizedStrings._rNlsKeys)) {
|
||||
for (let value of values) {
|
||||
context.report({ loc: value.call.loc, messageId: 'badKey', data: { key } });
|
||||
}
|
||||
}
|
||||
|
||||
// (2)
|
||||
// report all invalid duplicates (same key, different message)
|
||||
if (values.length > 1) {
|
||||
for (let i = 1; i < values.length; i++) {
|
||||
if (context.getSourceCode().getText(<any>values[i - 1].message) !== context.getSourceCode().getText(<any>values[i].message)) {
|
||||
context.report({ loc: values[i].call.loc, messageId: 'duplicateKey', data: { key } });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
['Literal']: (node: any) => collectDoubleQuotedStrings(node),
|
||||
['ExpressionStatement[directive] Literal:exit']: (node: any) => doubleQuotedStringLiterals.delete(node),
|
||||
['CallExpression[callee.type="MemberExpression"][callee.object.name="nls"][callee.property.name="localize"]:exit']: (node: any) => visitLocalizeCall(node),
|
||||
['CallExpression[callee.name="localize"][arguments.length>=2]:exit']: (node: any) => visitLocalizeCall(node),
|
||||
['Program:exit']: reportBadStringsAndBadKeys,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
145
build/lib/eslint/code-no-unused-expressions.js
Normal file
145
build/lib/eslint/code-no-unused-expressions.js
Normal file
@@ -0,0 +1,145 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// FORKED FROM https://github.com/eslint/eslint/blob/b23ad0d789a909baf8d7c41a35bc53df932eaf30/lib/rules/no-unused-expressions.js
|
||||
// and added support for `OptionalCallExpression`, see https://github.com/facebook/create-react-app/issues/8107 and https://github.com/eslint/eslint/issues/12642
|
||||
|
||||
/**
|
||||
* @fileoverview Flag expressions in statement position that do not side effect
|
||||
* @author Michael Ficarra
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
|
||||
docs: {
|
||||
description: 'disallow unused expressions',
|
||||
category: 'Best Practices',
|
||||
recommended: false,
|
||||
url: 'https://eslint.org/docs/rules/no-unused-expressions'
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
allowShortCircuit: {
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
allowTernary: {
|
||||
type: 'boolean',
|
||||
default: false
|
||||
},
|
||||
allowTaggedTemplates: {
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}
|
||||
},
|
||||
additionalProperties: false
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const config = context.options[0] || {},
|
||||
allowShortCircuit = config.allowShortCircuit || false,
|
||||
allowTernary = config.allowTernary || false,
|
||||
allowTaggedTemplates = config.allowTaggedTemplates || false;
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-description
|
||||
/**
|
||||
* @param {ASTNode} node any node
|
||||
* @returns {boolean} whether the given node structurally represents a directive
|
||||
*/
|
||||
function looksLikeDirective(node) {
|
||||
return node.type === 'ExpressionStatement' &&
|
||||
node.expression.type === 'Literal' && typeof node.expression.value === 'string';
|
||||
}
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-description
|
||||
/**
|
||||
* @param {Function} predicate ([a] -> Boolean) the function used to make the determination
|
||||
* @param {a[]} list the input list
|
||||
* @returns {a[]} the leading sequence of members in the given list that pass the given predicate
|
||||
*/
|
||||
function takeWhile(predicate, list) {
|
||||
for (let i = 0; i < list.length; ++i) {
|
||||
if (!predicate(list[i])) {
|
||||
return list.slice(0, i);
|
||||
}
|
||||
}
|
||||
return list.slice();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-description
|
||||
/**
|
||||
* @param {ASTNode} node a Program or BlockStatement node
|
||||
* @returns {ASTNode[]} the leading sequence of directive nodes in the given node's body
|
||||
*/
|
||||
function directives(node) {
|
||||
return takeWhile(looksLikeDirective, node.body);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line jsdoc/require-description
|
||||
/**
|
||||
* @param {ASTNode} node any node
|
||||
* @param {ASTNode[]} ancestors the given node's ancestors
|
||||
* @returns {boolean} whether the given node is considered a directive in its current position
|
||||
*/
|
||||
function isDirective(node, ancestors) {
|
||||
const parent = ancestors[ancestors.length - 1],
|
||||
grandparent = ancestors[ancestors.length - 2];
|
||||
|
||||
return (parent.type === 'Program' || parent.type === 'BlockStatement' &&
|
||||
(/Function/u.test(grandparent.type))) &&
|
||||
directives(parent).indexOf(node) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not a given node is a valid expression. Recurses on short circuit eval and ternary nodes if enabled by flags.
|
||||
* @param {ASTNode} node any node
|
||||
* @returns {boolean} whether the given node is a valid expression
|
||||
*/
|
||||
function isValidExpression(node) {
|
||||
if (allowTernary) {
|
||||
|
||||
// Recursive check for ternary and logical expressions
|
||||
if (node.type === 'ConditionalExpression') {
|
||||
return isValidExpression(node.consequent) && isValidExpression(node.alternate);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowShortCircuit) {
|
||||
if (node.type === 'LogicalExpression') {
|
||||
return isValidExpression(node.right);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowTaggedTemplates && node.type === 'TaggedTemplateExpression') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return /^(?:Assignment|OptionalCall|Call|New|Update|Yield|Await)Expression$/u.test(node.type) ||
|
||||
(node.type === 'UnaryExpression' && ['delete', 'void'].indexOf(node.operator) >= 0);
|
||||
}
|
||||
|
||||
return {
|
||||
ExpressionStatement(node) {
|
||||
if (!isValidExpression(node.expression) && !isDirective(node, context.getAncestors())) {
|
||||
context.report({ node, message: 'Expected an assignment or function call and instead saw an expression.' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
57
build/lib/eslint/code-translation-remind.js
Normal file
57
build/lib/eslint/code-translation-remind.js
Normal file
@@ -0,0 +1,57 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
var _a;
|
||||
const fs_1 = require("fs");
|
||||
const utils_1 = require("./utils");
|
||||
module.exports = new (_a = class TranslationRemind {
|
||||
constructor() {
|
||||
this.meta = {
|
||||
messages: {
|
||||
missing: 'Please add \'{{resource}}\' to ./build/lib/i18n.resources.json file to use translations here.'
|
||||
}
|
||||
};
|
||||
}
|
||||
create(context) {
|
||||
return utils_1.createImportRuleListener((node, path) => this._checkImport(context, node, path));
|
||||
}
|
||||
_checkImport(context, node, path) {
|
||||
if (path !== TranslationRemind.NLS_MODULE) {
|
||||
return;
|
||||
}
|
||||
const currentFile = context.getFilename();
|
||||
const matchService = currentFile.match(/vs\/workbench\/services\/\w+/);
|
||||
const matchPart = currentFile.match(/vs\/workbench\/contrib\/\w+/);
|
||||
if (!matchService && !matchPart) {
|
||||
return;
|
||||
}
|
||||
const resource = matchService ? matchService[0] : matchPart[0];
|
||||
let resourceDefined = false;
|
||||
let json;
|
||||
try {
|
||||
json = fs_1.readFileSync('./build/lib/i18n.resources.json', 'utf8');
|
||||
}
|
||||
catch (e) {
|
||||
console.error('[translation-remind rule]: File with resources to pull from Transifex was not found. Aborting translation resource check for newly defined workbench part/service.');
|
||||
return;
|
||||
}
|
||||
const workbenchResources = JSON.parse(json).workbench;
|
||||
workbenchResources.forEach((existingResource) => {
|
||||
if (existingResource.name === resource) {
|
||||
resourceDefined = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
if (!resourceDefined) {
|
||||
context.report({
|
||||
loc: node.loc,
|
||||
messageId: 'missing',
|
||||
data: { resource }
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
_a.NLS_MODULE = 'vs/nls',
|
||||
_a);
|
||||
67
build/lib/eslint/code-translation-remind.ts
Normal file
67
build/lib/eslint/code-translation-remind.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as eslint from 'eslint';
|
||||
import { TSESTree } from '@typescript-eslint/experimental-utils';
|
||||
import { readFileSync } from 'fs';
|
||||
import { createImportRuleListener } from './utils';
|
||||
|
||||
|
||||
export = new class TranslationRemind implements eslint.Rule.RuleModule {
|
||||
|
||||
private static NLS_MODULE = 'vs/nls';
|
||||
|
||||
readonly meta: eslint.Rule.RuleMetaData = {
|
||||
messages: {
|
||||
missing: 'Please add \'{{resource}}\' to ./build/lib/i18n.resources.json file to use translations here.'
|
||||
}
|
||||
};
|
||||
|
||||
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
|
||||
return createImportRuleListener((node, path) => this._checkImport(context, node, path));
|
||||
}
|
||||
|
||||
private _checkImport(context: eslint.Rule.RuleContext, node: TSESTree.Node, path: string) {
|
||||
|
||||
if (path !== TranslationRemind.NLS_MODULE) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentFile = context.getFilename();
|
||||
const matchService = currentFile.match(/vs\/workbench\/services\/\w+/);
|
||||
const matchPart = currentFile.match(/vs\/workbench\/contrib\/\w+/);
|
||||
if (!matchService && !matchPart) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resource = matchService ? matchService[0] : matchPart![0];
|
||||
let resourceDefined = false;
|
||||
|
||||
let json;
|
||||
try {
|
||||
json = readFileSync('./build/lib/i18n.resources.json', 'utf8');
|
||||
} catch (e) {
|
||||
console.error('[translation-remind rule]: File with resources to pull from Transifex was not found. Aborting translation resource check for newly defined workbench part/service.');
|
||||
return;
|
||||
}
|
||||
const workbenchResources = JSON.parse(json).workbench;
|
||||
|
||||
workbenchResources.forEach((existingResource: any) => {
|
||||
if (existingResource.name === resource) {
|
||||
resourceDefined = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (!resourceDefined) {
|
||||
context.report({
|
||||
loc: node.loc,
|
||||
messageId: 'missing',
|
||||
data: { resource }
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
36
build/lib/eslint/utils.js
Normal file
36
build/lib/eslint/utils.js
Normal file
@@ -0,0 +1,36 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
function createImportRuleListener(validateImport) {
|
||||
function _checkImport(node) {
|
||||
if (node && node.type === 'Literal' && typeof node.value === 'string') {
|
||||
validateImport(node, node.value);
|
||||
}
|
||||
}
|
||||
return {
|
||||
// import ??? from 'module'
|
||||
ImportDeclaration: (node) => {
|
||||
_checkImport(node.source);
|
||||
},
|
||||
// import('module').then(...) OR await import('module')
|
||||
['CallExpression[callee.type="Import"][arguments.length=1] > Literal']: (node) => {
|
||||
_checkImport(node);
|
||||
},
|
||||
// import foo = ...
|
||||
['TSImportEqualsDeclaration > TSExternalModuleReference > Literal']: (node) => {
|
||||
_checkImport(node);
|
||||
},
|
||||
// export ?? from 'module'
|
||||
ExportAllDeclaration: (node) => {
|
||||
_checkImport(node.source);
|
||||
},
|
||||
// export {foo} from 'module'
|
||||
ExportNamedDeclaration: (node) => {
|
||||
_checkImport(node.source);
|
||||
},
|
||||
};
|
||||
}
|
||||
exports.createImportRuleListener = createImportRuleListener;
|
||||
40
build/lib/eslint/utils.ts
Normal file
40
build/lib/eslint/utils.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as eslint from 'eslint';
|
||||
import { TSESTree } from '@typescript-eslint/experimental-utils';
|
||||
|
||||
export function createImportRuleListener(validateImport: (node: TSESTree.Literal, value: string) => any): eslint.Rule.RuleListener {
|
||||
|
||||
function _checkImport(node: TSESTree.Node | null) {
|
||||
if (node && node.type === 'Literal' && typeof node.value === 'string') {
|
||||
validateImport(node, node.value);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
// import ??? from 'module'
|
||||
ImportDeclaration: (node: any) => {
|
||||
_checkImport((<TSESTree.ImportDeclaration>node).source);
|
||||
},
|
||||
// import('module').then(...) OR await import('module')
|
||||
['CallExpression[callee.type="Import"][arguments.length=1] > Literal']: (node: any) => {
|
||||
_checkImport(node);
|
||||
},
|
||||
// import foo = ...
|
||||
['TSImportEqualsDeclaration > TSExternalModuleReference > Literal']: (node: any) => {
|
||||
_checkImport(node);
|
||||
},
|
||||
// export ?? from 'module'
|
||||
ExportAllDeclaration: (node: any) => {
|
||||
_checkImport((<TSESTree.ExportAllDeclaration>node).source);
|
||||
},
|
||||
// export {foo} from 'module'
|
||||
ExportNamedDeclaration: (node: any) => {
|
||||
_checkImport((<TSESTree.ExportNamedDeclaration>node).source);
|
||||
},
|
||||
|
||||
};
|
||||
}
|
||||
@@ -30,6 +30,14 @@
|
||||
"name": "vs/workbench/api/common",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/contrib/backup",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/contrib/bulkEdit",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/contrib/cli",
|
||||
"project": "vscode-workbench"
|
||||
@@ -309,6 +317,10 @@
|
||||
{
|
||||
"name": "vs/workbench/services/userData",
|
||||
"project": "vscode-workbench"
|
||||
},
|
||||
{
|
||||
"name": "vs/workbench/services/userDataSync",
|
||||
"project": "vscode-workbench"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
209
build/lib/layersChecker.js
Normal file
209
build/lib/layersChecker.js
Normal file
@@ -0,0 +1,209 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ts = require("typescript");
|
||||
const fs_1 = require("fs");
|
||||
const path_1 = require("path");
|
||||
const minimatch_1 = require("minimatch");
|
||||
//
|
||||
// #############################################################################################
|
||||
//
|
||||
// A custom typescript checker for the specific task of detecting the use of certain types in a
|
||||
// layer that does not allow such use. For example:
|
||||
// - using DOM globals in common/node/electron-main layer (e.g. HTMLElement)
|
||||
// - using node.js globals in common/browser layer (e.g. process)
|
||||
//
|
||||
// Make changes to below RULES to lift certain files from these checks only if absolutely needed
|
||||
//
|
||||
// #############################################################################################
|
||||
//
|
||||
// Types we assume are present in all implementations of JS VMs (node.js, browsers)
|
||||
// Feel free to add more core types as you see needed if present in node.js and browsers
|
||||
const CORE_TYPES = [
|
||||
'require',
|
||||
'atob',
|
||||
'btoa',
|
||||
'setTimeout',
|
||||
'clearTimeout',
|
||||
'setInterval',
|
||||
'clearInterval',
|
||||
'console',
|
||||
'log',
|
||||
'info',
|
||||
'warn',
|
||||
'error',
|
||||
'group',
|
||||
'groupEnd',
|
||||
'table',
|
||||
'Error',
|
||||
'String',
|
||||
'throws',
|
||||
'stack',
|
||||
'captureStackTrace',
|
||||
'stackTraceLimit',
|
||||
'TextDecoder',
|
||||
'TextEncoder',
|
||||
'encode',
|
||||
'decode',
|
||||
'self',
|
||||
'trimLeft',
|
||||
'trimRight'
|
||||
];
|
||||
const RULES = [
|
||||
// Tests: skip
|
||||
{
|
||||
target: '**/{vs,sql}/**/test/**',
|
||||
skip: true // -> skip all test files
|
||||
},
|
||||
// Common: vs/base/common/platform.ts
|
||||
{
|
||||
target: '**/vs/base/common/platform.ts',
|
||||
allowedTypes: [
|
||||
...CORE_TYPES,
|
||||
// Safe access to postMessage() and friends
|
||||
'MessageEvent',
|
||||
'data'
|
||||
],
|
||||
disallowedDefinitions: [
|
||||
'lib.dom.d.ts',
|
||||
'@types/node' // no node.js
|
||||
]
|
||||
},
|
||||
// Common: vs/workbench/api/common/extHostExtensionService.ts
|
||||
{
|
||||
target: '**/vs/workbench/api/common/extHostExtensionService.ts',
|
||||
allowedTypes: [
|
||||
...CORE_TYPES,
|
||||
// Safe access to global
|
||||
'global'
|
||||
],
|
||||
disallowedDefinitions: [
|
||||
'lib.dom.d.ts',
|
||||
'@types/node' // no node.js
|
||||
]
|
||||
},
|
||||
// Common
|
||||
{
|
||||
target: '**/{vs,sql}/**/common/**',
|
||||
allowedTypes: CORE_TYPES,
|
||||
disallowedDefinitions: [
|
||||
'lib.dom.d.ts',
|
||||
'@types/node' // no node.js
|
||||
]
|
||||
},
|
||||
// Browser
|
||||
{
|
||||
target: '**/{vs,sql}/**/browser/**',
|
||||
allowedTypes: CORE_TYPES,
|
||||
disallowedDefinitions: [
|
||||
'@types/node' // no node.js
|
||||
]
|
||||
},
|
||||
// node.js
|
||||
{
|
||||
target: '**/{vs,sql}/**/node/**',
|
||||
allowedTypes: [
|
||||
...CORE_TYPES,
|
||||
// --> types from node.d.ts that duplicate from lib.dom.d.ts
|
||||
'URL',
|
||||
'protocol',
|
||||
'hostname',
|
||||
'port',
|
||||
'pathname',
|
||||
'search',
|
||||
'username',
|
||||
'password'
|
||||
],
|
||||
disallowedDefinitions: [
|
||||
'lib.dom.d.ts' // no DOM
|
||||
]
|
||||
},
|
||||
// Electron (renderer): skip
|
||||
{
|
||||
target: '**/{vs,sql}/**/electron-browser/**',
|
||||
skip: true // -> supports all types
|
||||
},
|
||||
// Electron (main)
|
||||
{
|
||||
target: '**/{vs,sql}/**/electron-main/**',
|
||||
allowedTypes: [
|
||||
...CORE_TYPES,
|
||||
// --> types from electron.d.ts that duplicate from lib.dom.d.ts
|
||||
'Event',
|
||||
'Request'
|
||||
],
|
||||
disallowedDefinitions: [
|
||||
'lib.dom.d.ts' // no DOM
|
||||
]
|
||||
}
|
||||
];
|
||||
const TS_CONFIG_PATH = path_1.join(__dirname, '../../', 'src', 'tsconfig.json');
|
||||
let hasErrors = false;
|
||||
function checkFile(program, sourceFile, rule) {
|
||||
checkNode(sourceFile);
|
||||
function checkNode(node) {
|
||||
var _a;
|
||||
if (node.kind !== ts.SyntaxKind.Identifier) {
|
||||
return ts.forEachChild(node, checkNode); // recurse down
|
||||
}
|
||||
const text = node.getText(sourceFile);
|
||||
if ((_a = rule.allowedTypes) === null || _a === void 0 ? void 0 : _a.some(allowed => allowed === text)) {
|
||||
return; // override
|
||||
}
|
||||
const checker = program.getTypeChecker();
|
||||
const symbol = checker.getSymbolAtLocation(node);
|
||||
if (symbol) {
|
||||
const declarations = symbol.declarations;
|
||||
if (Array.isArray(declarations)) {
|
||||
for (const declaration of declarations) {
|
||||
if (declaration) {
|
||||
const parent = declaration.parent;
|
||||
if (parent) {
|
||||
const parentSourceFile = parent.getSourceFile();
|
||||
if (parentSourceFile) {
|
||||
const definitionFileName = parentSourceFile.fileName;
|
||||
if (rule.disallowedDefinitions) {
|
||||
for (const disallowedDefinition of rule.disallowedDefinitions) {
|
||||
if (definitionFileName.indexOf(disallowedDefinition) >= 0) {
|
||||
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
||||
console.log(`[build/lib/layersChecker.ts]: Reference to '${text}' from '${disallowedDefinition}' violates layer '${rule.target}' (${sourceFile.fileName} (${line + 1},${character + 1})`);
|
||||
hasErrors = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function createProgram(tsconfigPath) {
|
||||
const tsConfig = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
||||
const configHostParser = { fileExists: fs_1.existsSync, readDirectory: ts.sys.readDirectory, readFile: file => fs_1.readFileSync(file, 'utf8'), useCaseSensitiveFileNames: process.platform === 'linux' };
|
||||
const tsConfigParsed = ts.parseJsonConfigFileContent(tsConfig.config, configHostParser, path_1.resolve(path_1.dirname(tsconfigPath)), { noEmit: true });
|
||||
const compilerHost = ts.createCompilerHost(tsConfigParsed.options, true);
|
||||
return ts.createProgram(tsConfigParsed.fileNames, tsConfigParsed.options, compilerHost);
|
||||
}
|
||||
//
|
||||
// Create program and start checking
|
||||
//
|
||||
const program = createProgram(TS_CONFIG_PATH);
|
||||
for (const sourceFile of program.getSourceFiles()) {
|
||||
for (const rule of RULES) {
|
||||
if (minimatch_1.match([sourceFile.fileName], rule.target).length > 0) {
|
||||
if (!rule.skip) {
|
||||
checkFile(program, sourceFile, rule);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasErrors) {
|
||||
process.exit(1);
|
||||
}
|
||||
245
build/lib/layersChecker.ts
Normal file
245
build/lib/layersChecker.ts
Normal file
@@ -0,0 +1,245 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import { readFileSync, existsSync } from 'fs';
|
||||
import { resolve, dirname, join } from 'path';
|
||||
import { match } from 'minimatch';
|
||||
|
||||
//
|
||||
// #############################################################################################
|
||||
//
|
||||
// A custom typescript checker for the specific task of detecting the use of certain types in a
|
||||
// layer that does not allow such use. For example:
|
||||
// - using DOM globals in common/node/electron-main layer (e.g. HTMLElement)
|
||||
// - using node.js globals in common/browser layer (e.g. process)
|
||||
//
|
||||
// Make changes to below RULES to lift certain files from these checks only if absolutely needed
|
||||
//
|
||||
// #############################################################################################
|
||||
//
|
||||
|
||||
// Types we assume are present in all implementations of JS VMs (node.js, browsers)
|
||||
// Feel free to add more core types as you see needed if present in node.js and browsers
|
||||
const CORE_TYPES = [
|
||||
'require', // from our AMD loader
|
||||
'atob',
|
||||
'btoa',
|
||||
'setTimeout',
|
||||
'clearTimeout',
|
||||
'setInterval',
|
||||
'clearInterval',
|
||||
'console',
|
||||
'log',
|
||||
'info',
|
||||
'warn',
|
||||
'error',
|
||||
'group',
|
||||
'groupEnd',
|
||||
'table',
|
||||
'Error',
|
||||
'String',
|
||||
'throws',
|
||||
'stack',
|
||||
'captureStackTrace',
|
||||
'stackTraceLimit',
|
||||
'TextDecoder',
|
||||
'TextEncoder',
|
||||
'encode',
|
||||
'decode',
|
||||
'self',
|
||||
'trimLeft',
|
||||
'trimRight'
|
||||
];
|
||||
|
||||
const RULES = [
|
||||
|
||||
// Tests: skip
|
||||
{
|
||||
target: '**/{vs,sql}/**/test/**',
|
||||
skip: true // -> skip all test files
|
||||
},
|
||||
|
||||
// Common: vs/base/common/platform.ts
|
||||
{
|
||||
target: '**/vs/base/common/platform.ts',
|
||||
allowedTypes: [
|
||||
...CORE_TYPES,
|
||||
|
||||
// Safe access to postMessage() and friends
|
||||
'MessageEvent',
|
||||
'data'
|
||||
],
|
||||
disallowedDefinitions: [
|
||||
'lib.dom.d.ts', // no DOM
|
||||
'@types/node' // no node.js
|
||||
]
|
||||
},
|
||||
|
||||
// Common: vs/workbench/api/common/extHostExtensionService.ts
|
||||
{
|
||||
target: '**/vs/workbench/api/common/extHostExtensionService.ts',
|
||||
allowedTypes: [
|
||||
...CORE_TYPES,
|
||||
|
||||
// Safe access to global
|
||||
'global'
|
||||
],
|
||||
disallowedDefinitions: [
|
||||
'lib.dom.d.ts', // no DOM
|
||||
'@types/node' // no node.js
|
||||
]
|
||||
},
|
||||
|
||||
// Common
|
||||
{
|
||||
target: '**/{vs,sql}/**/common/**',
|
||||
allowedTypes: CORE_TYPES,
|
||||
disallowedDefinitions: [
|
||||
'lib.dom.d.ts', // no DOM
|
||||
'@types/node' // no node.js
|
||||
]
|
||||
},
|
||||
|
||||
// Browser
|
||||
{
|
||||
target: '**/{vs,sql}/**/browser/**',
|
||||
allowedTypes: CORE_TYPES,
|
||||
disallowedDefinitions: [
|
||||
'@types/node' // no node.js
|
||||
]
|
||||
},
|
||||
|
||||
// node.js
|
||||
{
|
||||
target: '**/{vs,sql}/**/node/**',
|
||||
allowedTypes: [
|
||||
...CORE_TYPES,
|
||||
|
||||
// --> types from node.d.ts that duplicate from lib.dom.d.ts
|
||||
'URL',
|
||||
'protocol',
|
||||
'hostname',
|
||||
'port',
|
||||
'pathname',
|
||||
'search',
|
||||
'username',
|
||||
'password'
|
||||
],
|
||||
disallowedDefinitions: [
|
||||
'lib.dom.d.ts' // no DOM
|
||||
]
|
||||
},
|
||||
|
||||
// Electron (renderer): skip
|
||||
{
|
||||
target: '**/{vs,sql}/**/electron-browser/**',
|
||||
skip: true // -> supports all types
|
||||
},
|
||||
|
||||
// Electron (main)
|
||||
{
|
||||
target: '**/{vs,sql}/**/electron-main/**',
|
||||
allowedTypes: [
|
||||
...CORE_TYPES,
|
||||
|
||||
// --> types from electron.d.ts that duplicate from lib.dom.d.ts
|
||||
'Event',
|
||||
'Request'
|
||||
],
|
||||
disallowedDefinitions: [
|
||||
'lib.dom.d.ts' // no DOM
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const TS_CONFIG_PATH = join(__dirname, '../../', 'src', 'tsconfig.json');
|
||||
|
||||
interface IRule {
|
||||
target: string;
|
||||
skip?: boolean;
|
||||
allowedTypes?: string[];
|
||||
disallowedDefinitions?: string[];
|
||||
}
|
||||
|
||||
let hasErrors = false;
|
||||
|
||||
function checkFile(program: ts.Program, sourceFile: ts.SourceFile, rule: IRule) {
|
||||
checkNode(sourceFile);
|
||||
|
||||
function checkNode(node: ts.Node): void {
|
||||
if (node.kind !== ts.SyntaxKind.Identifier) {
|
||||
return ts.forEachChild(node, checkNode); // recurse down
|
||||
}
|
||||
|
||||
const text = node.getText(sourceFile);
|
||||
|
||||
if (rule.allowedTypes?.some(allowed => allowed === text)) {
|
||||
return; // override
|
||||
}
|
||||
|
||||
const checker = program.getTypeChecker();
|
||||
const symbol = checker.getSymbolAtLocation(node);
|
||||
if (symbol) {
|
||||
const declarations = symbol.declarations;
|
||||
if (Array.isArray(declarations)) {
|
||||
for (const declaration of declarations) {
|
||||
if (declaration) {
|
||||
const parent = declaration.parent;
|
||||
if (parent) {
|
||||
const parentSourceFile = parent.getSourceFile();
|
||||
if (parentSourceFile) {
|
||||
const definitionFileName = parentSourceFile.fileName;
|
||||
if (rule.disallowedDefinitions) {
|
||||
for (const disallowedDefinition of rule.disallowedDefinitions) {
|
||||
if (definitionFileName.indexOf(disallowedDefinition) >= 0) {
|
||||
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
||||
console.log(`[build/lib/layersChecker.ts]: Reference to '${text}' from '${disallowedDefinition}' violates layer '${rule.target}' (${sourceFile.fileName} (${line + 1},${character + 1})`);
|
||||
|
||||
hasErrors = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createProgram(tsconfigPath: string): ts.Program {
|
||||
const tsConfig = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
||||
|
||||
const configHostParser: ts.ParseConfigHost = { fileExists: existsSync, readDirectory: ts.sys.readDirectory, readFile: file => readFileSync(file, 'utf8'), useCaseSensitiveFileNames: process.platform === 'linux' };
|
||||
const tsConfigParsed = ts.parseJsonConfigFileContent(tsConfig.config, configHostParser, resolve(dirname(tsconfigPath)), { noEmit: true });
|
||||
|
||||
const compilerHost = ts.createCompilerHost(tsConfigParsed.options, true);
|
||||
|
||||
return ts.createProgram(tsConfigParsed.fileNames, tsConfigParsed.options, compilerHost);
|
||||
}
|
||||
|
||||
//
|
||||
// Create program and start checking
|
||||
//
|
||||
const program = createProgram(TS_CONFIG_PATH);
|
||||
|
||||
for (const sourceFile of program.getSourceFiles()) {
|
||||
for (const rule of RULES) {
|
||||
if (match([sourceFile.fileName], rule.target).length > 0) {
|
||||
if (!rule.skip) {
|
||||
checkFile(program, sourceFile, rule);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasErrors) {
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const Lint = require("tslint");
|
||||
class AbstractGlobalsRuleWalker extends Lint.RuleWalker {
|
||||
constructor(file, program, opts, _config) {
|
||||
super(file, opts);
|
||||
this.program = program;
|
||||
this._config = _config;
|
||||
}
|
||||
visitIdentifier(node) {
|
||||
if (this.getDisallowedGlobals().some(disallowedGlobal => disallowedGlobal === node.text)) {
|
||||
if (this._config.allowed && this._config.allowed.some(allowed => allowed === node.text)) {
|
||||
return; // override
|
||||
}
|
||||
const checker = this.program.getTypeChecker();
|
||||
const symbol = checker.getSymbolAtLocation(node);
|
||||
if (symbol) {
|
||||
const declarations = symbol.declarations;
|
||||
if (Array.isArray(declarations) && symbol.declarations.some(declaration => {
|
||||
if (declaration) {
|
||||
const parent = declaration.parent;
|
||||
if (parent) {
|
||||
const sourceFile = parent.getSourceFile();
|
||||
if (sourceFile) {
|
||||
const fileName = sourceFile.fileName;
|
||||
if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})) {
|
||||
this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
super.visitIdentifier(node);
|
||||
}
|
||||
}
|
||||
exports.AbstractGlobalsRuleWalker = AbstractGlobalsRuleWalker;
|
||||
@@ -1,57 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as Lint from 'tslint';
|
||||
|
||||
interface AbstractGlobalsRuleConfig {
|
||||
target: string;
|
||||
allowed: string[];
|
||||
}
|
||||
|
||||
export abstract class AbstractGlobalsRuleWalker extends Lint.RuleWalker {
|
||||
|
||||
constructor(file: ts.SourceFile, private program: ts.Program, opts: Lint.IOptions, private _config: AbstractGlobalsRuleConfig) {
|
||||
super(file, opts);
|
||||
}
|
||||
|
||||
protected abstract getDisallowedGlobals(): string[];
|
||||
|
||||
protected abstract getDefinitionPattern(): string;
|
||||
|
||||
visitIdentifier(node: ts.Identifier) {
|
||||
if (this.getDisallowedGlobals().some(disallowedGlobal => disallowedGlobal === node.text)) {
|
||||
if (this._config.allowed && this._config.allowed.some(allowed => allowed === node.text)) {
|
||||
return; // override
|
||||
}
|
||||
|
||||
const checker = this.program.getTypeChecker();
|
||||
const symbol = checker.getSymbolAtLocation(node);
|
||||
if (symbol) {
|
||||
const declarations = symbol.declarations;
|
||||
if (Array.isArray(declarations) && symbol.declarations.some(declaration => {
|
||||
if (declaration) {
|
||||
const parent = declaration.parent;
|
||||
if (parent) {
|
||||
const sourceFile = parent.getSourceFile();
|
||||
if (sourceFile) {
|
||||
const fileName = sourceFile.fileName;
|
||||
if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
})) {
|
||||
this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.visitIdentifier(node);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ts = require("typescript");
|
||||
const Lint = require("tslint");
|
||||
/**
|
||||
* Implementation of the double-quoted-string-arg rule which verifies that the specified index of calls matching
|
||||
* the specified signatures is quoted with double-quotes only.
|
||||
*/
|
||||
class Rule extends Lint.Rules.AbstractRule {
|
||||
apply(sourceFile) {
|
||||
return this.applyWithWalker(new DoubleQuotedStringArgRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
}
|
||||
exports.Rule = Rule;
|
||||
class DoubleQuotedStringArgRuleWalker extends Lint.RuleWalker {
|
||||
constructor(file, opts) {
|
||||
super(file, opts);
|
||||
this.signatures = Object.create(null);
|
||||
this.argIndex = undefined;
|
||||
const options = this.getOptions();
|
||||
const first = options && options.length > 0 ? options[0] : null;
|
||||
if (first) {
|
||||
if (Array.isArray(first.signatures)) {
|
||||
first.signatures.forEach((signature) => this.signatures[signature] = true);
|
||||
}
|
||||
if (typeof first.argIndex !== 'undefined') {
|
||||
this.argIndex = first.argIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
visitCallExpression(node) {
|
||||
this.checkCallExpression(node);
|
||||
super.visitCallExpression(node);
|
||||
}
|
||||
checkCallExpression(node) {
|
||||
// Not one of the functions we're looking for, continue on
|
||||
const functionName = node.expression.getText();
|
||||
if (functionName && !this.signatures[functionName]) {
|
||||
return;
|
||||
}
|
||||
const arg = node.arguments[this.argIndex];
|
||||
// Ignore if the arg isn't a string - we expect the compiler to warn if that's an issue
|
||||
if (arg && ts.isStringLiteral(arg)) {
|
||||
const argText = arg.getText();
|
||||
const doubleQuotedArg = argText.length >= 2 && argText[0] === DoubleQuotedStringArgRuleWalker.DOUBLE_QUOTE && argText[argText.length - 1] === DoubleQuotedStringArgRuleWalker.DOUBLE_QUOTE;
|
||||
if (!doubleQuotedArg) {
|
||||
const fix = Lint.Replacement.replaceFromTo(arg.getStart(), arg.getEnd(), `"${arg.getText().slice(1, arg.getWidth() - 1)}"`);
|
||||
this.addFailure(this.createFailure(arg.getStart(), arg.getWidth(), `Argument ${this.argIndex + 1} to '${functionName}' must be double quoted.`, fix));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DoubleQuotedStringArgRuleWalker.DOUBLE_QUOTE = '"';
|
||||
@@ -1,80 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as Lint from 'tslint';
|
||||
|
||||
/**
|
||||
* Implementation of the double-quoted-string-arg rule which verifies that the specified index of calls matching
|
||||
* the specified signatures is quoted with double-quotes only.
|
||||
*/
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
return this.applyWithWalker(new DoubleQuotedStringArgRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
}
|
||||
|
||||
interface Map<V> {
|
||||
[key: string]: V;
|
||||
}
|
||||
|
||||
interface DoubleQuotedStringArgOptions {
|
||||
signatures?: string[];
|
||||
argIndex?: number;
|
||||
}
|
||||
|
||||
class DoubleQuotedStringArgRuleWalker extends Lint.RuleWalker {
|
||||
|
||||
private static DOUBLE_QUOTE: string = '"';
|
||||
|
||||
private signatures: Map<boolean>;
|
||||
private argIndex: number | undefined;
|
||||
|
||||
constructor(file: ts.SourceFile, opts: Lint.IOptions) {
|
||||
super(file, opts);
|
||||
this.signatures = Object.create(null);
|
||||
this.argIndex = undefined;
|
||||
const options: any[] = this.getOptions();
|
||||
const first: DoubleQuotedStringArgOptions = options && options.length > 0 ? options[0] : null;
|
||||
if (first) {
|
||||
if (Array.isArray(first.signatures)) {
|
||||
first.signatures.forEach((signature: string) => this.signatures[signature] = true);
|
||||
}
|
||||
if (typeof first.argIndex !== 'undefined') {
|
||||
this.argIndex = first.argIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected visitCallExpression(node: ts.CallExpression): void {
|
||||
this.checkCallExpression(node);
|
||||
super.visitCallExpression(node);
|
||||
}
|
||||
|
||||
private checkCallExpression(node: ts.CallExpression): void {
|
||||
// Not one of the functions we're looking for, continue on
|
||||
const functionName = node.expression.getText();
|
||||
if (functionName && !this.signatures[functionName]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const arg = node.arguments[this.argIndex!];
|
||||
|
||||
// Ignore if the arg isn't a string - we expect the compiler to warn if that's an issue
|
||||
if(arg && ts.isStringLiteral(arg)) {
|
||||
const argText = arg.getText();
|
||||
const doubleQuotedArg = argText.length >= 2 && argText[0] === DoubleQuotedStringArgRuleWalker.DOUBLE_QUOTE && argText[argText.length - 1] === DoubleQuotedStringArgRuleWalker.DOUBLE_QUOTE;
|
||||
|
||||
if (!doubleQuotedArg) {
|
||||
const fix = Lint.Replacement.replaceFromTo(arg.getStart(), arg.getEnd(), `"${arg.getText().slice(1, arg.getWidth() - 1)}"`);
|
||||
this.addFailure(this.createFailure(
|
||||
arg.getStart(), arg.getWidth(),
|
||||
`Argument ${this.argIndex! + 1} to '${functionName}' must be double quoted.`, fix));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const path_1 = require("path");
|
||||
const Lint = require("tslint");
|
||||
class Rule extends Lint.Rules.AbstractRule {
|
||||
apply(sourceFile) {
|
||||
return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions()));
|
||||
}
|
||||
}
|
||||
exports.Rule = Rule;
|
||||
class ImportPatterns extends Lint.RuleWalker {
|
||||
constructor(file, opts) {
|
||||
super(file, opts);
|
||||
this.imports = Object.create(null);
|
||||
}
|
||||
visitImportDeclaration(node) {
|
||||
let path = node.moduleSpecifier.getText();
|
||||
// remove quotes
|
||||
path = path.slice(1, -1);
|
||||
if (path[0] === '.') {
|
||||
path = path_1.join(path_1.dirname(node.getSourceFile().fileName), path);
|
||||
}
|
||||
if (this.imports[path]) {
|
||||
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Duplicate imports for '${path}'.`));
|
||||
}
|
||||
this.imports[path] = true;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import { join, dirname } from 'path';
|
||||
import * as Lint from 'tslint';
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions()));
|
||||
}
|
||||
}
|
||||
|
||||
class ImportPatterns extends Lint.RuleWalker {
|
||||
|
||||
private imports: { [path: string]: boolean; } = Object.create(null);
|
||||
|
||||
constructor(file: ts.SourceFile, opts: Lint.IOptions) {
|
||||
super(file, opts);
|
||||
}
|
||||
|
||||
protected visitImportDeclaration(node: ts.ImportDeclaration): void {
|
||||
let path = node.moduleSpecifier.getText();
|
||||
|
||||
// remove quotes
|
||||
path = path.slice(1, -1);
|
||||
|
||||
if (path[0] === '.') {
|
||||
path = join(dirname(node.getSourceFile().fileName), path);
|
||||
}
|
||||
|
||||
if (this.imports[path]) {
|
||||
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Duplicate imports for '${path}'.`));
|
||||
}
|
||||
|
||||
this.imports[path] = true;
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ts = require("typescript");
|
||||
const Lint = require("tslint");
|
||||
const minimatch = require("minimatch");
|
||||
const path_1 = require("path");
|
||||
class Rule extends Lint.Rules.AbstractRule {
|
||||
apply(sourceFile) {
|
||||
const configs = this.getOptions().ruleArguments;
|
||||
for (const config of configs) {
|
||||
if (minimatch(sourceFile.fileName, config.target)) {
|
||||
return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions(), config));
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
exports.Rule = Rule;
|
||||
class ImportPatterns extends Lint.RuleWalker {
|
||||
constructor(file, opts, _config) {
|
||||
super(file, opts);
|
||||
this._config = _config;
|
||||
}
|
||||
visitImportEqualsDeclaration(node) {
|
||||
if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) {
|
||||
this._validateImport(node.moduleReference.expression.getText(), node);
|
||||
}
|
||||
}
|
||||
visitImportDeclaration(node) {
|
||||
this._validateImport(node.moduleSpecifier.getText(), node);
|
||||
}
|
||||
visitCallExpression(node) {
|
||||
super.visitCallExpression(node);
|
||||
// import('foo') statements inside the code
|
||||
if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
||||
const [path] = node.arguments;
|
||||
this._validateImport(path.getText(), node);
|
||||
}
|
||||
}
|
||||
_validateImport(path, node) {
|
||||
// remove quotes
|
||||
path = path.slice(1, -1);
|
||||
// resolve relative paths
|
||||
if (path[0] === '.') {
|
||||
path = path_1.join(this.getSourceFile().fileName, path);
|
||||
}
|
||||
let restrictions;
|
||||
if (typeof this._config.restrictions === 'string') {
|
||||
restrictions = [this._config.restrictions];
|
||||
}
|
||||
else {
|
||||
restrictions = this._config.restrictions;
|
||||
}
|
||||
let matched = false;
|
||||
for (const pattern of restrictions) {
|
||||
if (minimatch(path, pattern)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
// None of the restrictions matched
|
||||
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Imports violates '${restrictions.join(' or ')}' restrictions. See https://github.com/Microsoft/vscode/wiki/Code-Organization`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as Lint from 'tslint';
|
||||
import * as minimatch from 'minimatch';
|
||||
import { join } from 'path';
|
||||
|
||||
interface ImportPatternsConfig {
|
||||
target: string;
|
||||
restrictions: string | string[];
|
||||
}
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
|
||||
const configs = <ImportPatternsConfig[]>this.getOptions().ruleArguments;
|
||||
|
||||
|
||||
for (const config of configs) {
|
||||
if (minimatch(sourceFile.fileName, config.target)) {
|
||||
return this.applyWithWalker(new ImportPatterns(sourceFile, this.getOptions(), config));
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
class ImportPatterns extends Lint.RuleWalker {
|
||||
|
||||
constructor(file: ts.SourceFile, opts: Lint.IOptions, private _config: ImportPatternsConfig) {
|
||||
super(file, opts);
|
||||
}
|
||||
|
||||
protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void {
|
||||
if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) {
|
||||
this._validateImport(node.moduleReference.expression.getText(), node);
|
||||
}
|
||||
}
|
||||
|
||||
protected visitImportDeclaration(node: ts.ImportDeclaration): void {
|
||||
this._validateImport(node.moduleSpecifier.getText(), node);
|
||||
}
|
||||
|
||||
protected visitCallExpression(node: ts.CallExpression): void {
|
||||
super.visitCallExpression(node);
|
||||
|
||||
// import('foo') statements inside the code
|
||||
if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
||||
const [path] = node.arguments;
|
||||
this._validateImport(path.getText(), node);
|
||||
}
|
||||
}
|
||||
|
||||
private _validateImport(path: string, node: ts.Node): void {
|
||||
// remove quotes
|
||||
path = path.slice(1, -1);
|
||||
|
||||
// resolve relative paths
|
||||
if (path[0] === '.') {
|
||||
path = join(this.getSourceFile().fileName, path);
|
||||
}
|
||||
|
||||
let restrictions: string[];
|
||||
if (typeof this._config.restrictions === 'string') {
|
||||
restrictions = [this._config.restrictions];
|
||||
} else {
|
||||
restrictions = this._config.restrictions;
|
||||
}
|
||||
|
||||
let matched = false;
|
||||
for (const pattern of restrictions) {
|
||||
if (minimatch(path, pattern)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
// None of the restrictions matched
|
||||
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Imports violates '${restrictions.join(' or ')}' restrictions. See https://github.com/Microsoft/vscode/wiki/Code-Organization`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ts = require("typescript");
|
||||
const Lint = require("tslint");
|
||||
const path_1 = require("path");
|
||||
class Rule extends Lint.Rules.AbstractRule {
|
||||
apply(sourceFile) {
|
||||
const parts = path_1.dirname(sourceFile.fileName).split(/\\|\//);
|
||||
const ruleArgs = this.getOptions().ruleArguments[0];
|
||||
let config;
|
||||
for (let i = parts.length - 1; i >= 0; i--) {
|
||||
if (ruleArgs[parts[i]]) {
|
||||
config = {
|
||||
allowed: new Set(ruleArgs[parts[i]]).add(parts[i]),
|
||||
disallowed: new Set()
|
||||
};
|
||||
Object.keys(ruleArgs).forEach(key => {
|
||||
if (!config.allowed.has(key)) {
|
||||
config.disallowed.add(key);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!config) {
|
||||
return [];
|
||||
}
|
||||
return this.applyWithWalker(new LayeringRule(sourceFile, config, this.getOptions()));
|
||||
}
|
||||
}
|
||||
exports.Rule = Rule;
|
||||
class LayeringRule extends Lint.RuleWalker {
|
||||
constructor(file, config, opts) {
|
||||
super(file, opts);
|
||||
this._config = config;
|
||||
}
|
||||
visitImportEqualsDeclaration(node) {
|
||||
if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) {
|
||||
this._validateImport(node.moduleReference.expression.getText(), node);
|
||||
}
|
||||
}
|
||||
visitImportDeclaration(node) {
|
||||
this._validateImport(node.moduleSpecifier.getText(), node);
|
||||
}
|
||||
visitCallExpression(node) {
|
||||
super.visitCallExpression(node);
|
||||
// import('foo') statements inside the code
|
||||
if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
||||
const [path] = node.arguments;
|
||||
this._validateImport(path.getText(), node);
|
||||
}
|
||||
}
|
||||
_validateImport(path, node) {
|
||||
// remove quotes
|
||||
path = path.slice(1, -1);
|
||||
if (path[0] === '.') {
|
||||
path = path_1.join(path_1.dirname(node.getSourceFile().fileName), path);
|
||||
}
|
||||
const parts = path_1.dirname(path).split(/\\|\//);
|
||||
for (let i = parts.length - 1; i >= 0; i--) {
|
||||
const part = parts[i];
|
||||
if (this._config.allowed.has(part)) {
|
||||
// GOOD - same layer
|
||||
return;
|
||||
}
|
||||
if (this._config.disallowed.has(part)) {
|
||||
// BAD - wrong layer
|
||||
const message = `Bad layering. You are not allowed to access '${part}' from here, allowed layers are: [${LayeringRule._print(this._config.allowed)}]`;
|
||||
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
static _print(set) {
|
||||
const r = [];
|
||||
set.forEach(e => r.push(e));
|
||||
return r.join(', ');
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as Lint from 'tslint';
|
||||
import { join, dirname } from 'path';
|
||||
|
||||
interface Config {
|
||||
allowed: Set<string>;
|
||||
disallowed: Set<string>;
|
||||
}
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
|
||||
const parts = dirname(sourceFile.fileName).split(/\\|\//);
|
||||
const ruleArgs = this.getOptions().ruleArguments[0];
|
||||
|
||||
let config: Config | undefined;
|
||||
for (let i = parts.length - 1; i >= 0; i--) {
|
||||
if (ruleArgs[parts[i]]) {
|
||||
config = {
|
||||
allowed: new Set<string>(<string[]>ruleArgs[parts[i]]).add(parts[i]),
|
||||
disallowed: new Set<string>()
|
||||
};
|
||||
Object.keys(ruleArgs).forEach(key => {
|
||||
if (!config!.allowed.has(key)) {
|
||||
config!.disallowed.add(key);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!config) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return this.applyWithWalker(new LayeringRule(sourceFile, config, this.getOptions()));
|
||||
}
|
||||
}
|
||||
|
||||
class LayeringRule extends Lint.RuleWalker {
|
||||
|
||||
private _config: Config;
|
||||
|
||||
constructor(file: ts.SourceFile, config: Config, opts: Lint.IOptions) {
|
||||
super(file, opts);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void {
|
||||
if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) {
|
||||
this._validateImport(node.moduleReference.expression.getText(), node);
|
||||
}
|
||||
}
|
||||
|
||||
protected visitImportDeclaration(node: ts.ImportDeclaration): void {
|
||||
this._validateImport(node.moduleSpecifier.getText(), node);
|
||||
}
|
||||
|
||||
protected visitCallExpression(node: ts.CallExpression): void {
|
||||
super.visitCallExpression(node);
|
||||
|
||||
// import('foo') statements inside the code
|
||||
if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
||||
const [path] = node.arguments;
|
||||
this._validateImport(path.getText(), node);
|
||||
}
|
||||
}
|
||||
|
||||
private _validateImport(path: string, node: ts.Node): void {
|
||||
// remove quotes
|
||||
path = path.slice(1, -1);
|
||||
|
||||
if (path[0] === '.') {
|
||||
path = join(dirname(node.getSourceFile().fileName), path);
|
||||
}
|
||||
|
||||
const parts = dirname(path).split(/\\|\//);
|
||||
for (let i = parts.length - 1; i >= 0; i--) {
|
||||
const part = parts[i];
|
||||
|
||||
if (this._config.allowed.has(part)) {
|
||||
// GOOD - same layer
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._config.disallowed.has(part)) {
|
||||
// BAD - wrong layer
|
||||
const message = `Bad layering. You are not allowed to access '${part}' from here, allowed layers are: [${LayeringRule._print(this._config.allowed)}]`;
|
||||
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), message));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static _print(set: Set<string>): string {
|
||||
const r: string[] = [];
|
||||
set.forEach(e => r.push(e));
|
||||
return r.join(', ');
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const Lint = require("tslint");
|
||||
const minimatch = require("minimatch");
|
||||
const abstractGlobalsRule_1 = require("./abstractGlobalsRule");
|
||||
class Rule extends Lint.Rules.TypedRule {
|
||||
applyWithProgram(sourceFile, program) {
|
||||
const configs = this.getOptions().ruleArguments;
|
||||
for (const config of configs) {
|
||||
if (minimatch(sourceFile.fileName, config.target)) {
|
||||
return this.applyWithWalker(new NoDOMGlobalsRuleWalker(sourceFile, program, this.getOptions(), config));
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
exports.Rule = Rule;
|
||||
class NoDOMGlobalsRuleWalker extends abstractGlobalsRule_1.AbstractGlobalsRuleWalker {
|
||||
getDefinitionPattern() {
|
||||
return 'lib.dom.d.ts';
|
||||
}
|
||||
getDisallowedGlobals() {
|
||||
// intentionally not complete
|
||||
return [
|
||||
"window",
|
||||
"document",
|
||||
"HTMLElement"
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as Lint from 'tslint';
|
||||
import * as minimatch from 'minimatch';
|
||||
import { AbstractGlobalsRuleWalker } from './abstractGlobalsRule';
|
||||
|
||||
interface NoDOMGlobalsRuleConfig {
|
||||
target: string;
|
||||
allowed: string[];
|
||||
}
|
||||
|
||||
export class Rule extends Lint.Rules.TypedRule {
|
||||
|
||||
applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
|
||||
const configs = <NoDOMGlobalsRuleConfig[]>this.getOptions().ruleArguments;
|
||||
|
||||
for (const config of configs) {
|
||||
if (minimatch(sourceFile.fileName, config.target)) {
|
||||
return this.applyWithWalker(new NoDOMGlobalsRuleWalker(sourceFile, program, this.getOptions(), config));
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
class NoDOMGlobalsRuleWalker extends AbstractGlobalsRuleWalker {
|
||||
|
||||
getDefinitionPattern(): string {
|
||||
return 'lib.dom.d.ts';
|
||||
}
|
||||
|
||||
getDisallowedGlobals(): string[] {
|
||||
// intentionally not complete
|
||||
return [
|
||||
"window",
|
||||
"document",
|
||||
"HTMLElement"
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ts = require("typescript");
|
||||
const Lint = require("tslint");
|
||||
class Rule extends Lint.Rules.AbstractRule {
|
||||
apply(sourceFile) {
|
||||
return this.applyWithWalker(new NewBufferRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
}
|
||||
exports.Rule = Rule;
|
||||
class NewBufferRuleWalker extends Lint.RuleWalker {
|
||||
visitNewExpression(node) {
|
||||
if (node.expression.kind === ts.SyntaxKind.Identifier && node.expression && node.expression.text === 'Buffer') {
|
||||
this.addFailureAtNode(node, '`new Buffer` is deprecated. Consider Buffer.From or Buffer.alloc instead.');
|
||||
}
|
||||
super.visitNewExpression(node);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as Lint from 'tslint';
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
return this.applyWithWalker(new NewBufferRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
}
|
||||
|
||||
class NewBufferRuleWalker extends Lint.RuleWalker {
|
||||
visitNewExpression(node: ts.NewExpression) {
|
||||
if (node.expression.kind === ts.SyntaxKind.Identifier && node.expression && (node.expression as ts.Identifier).text === 'Buffer') {
|
||||
this.addFailureAtNode(node, '`new Buffer` is deprecated. Consider Buffer.From or Buffer.alloc instead.');
|
||||
}
|
||||
|
||||
super.visitNewExpression(node);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ts = require("typescript");
|
||||
const Lint = require("tslint");
|
||||
const path_1 = require("path");
|
||||
class Rule extends Lint.Rules.AbstractRule {
|
||||
apply(sourceFile) {
|
||||
if (/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(sourceFile.fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(sourceFile.fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(sourceFile.fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(sourceFile.fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(sourceFile.fileName)) {
|
||||
return this.applyWithWalker(new NoNlsInStandaloneEditorRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
exports.Rule = Rule;
|
||||
class NoNlsInStandaloneEditorRuleWalker extends Lint.RuleWalker {
|
||||
constructor(file, opts) {
|
||||
super(file, opts);
|
||||
}
|
||||
visitImportEqualsDeclaration(node) {
|
||||
if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) {
|
||||
this._validateImport(node.moduleReference.expression.getText(), node);
|
||||
}
|
||||
}
|
||||
visitImportDeclaration(node) {
|
||||
this._validateImport(node.moduleSpecifier.getText(), node);
|
||||
}
|
||||
visitCallExpression(node) {
|
||||
super.visitCallExpression(node);
|
||||
// import('foo') statements inside the code
|
||||
if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
||||
const [path] = node.arguments;
|
||||
this._validateImport(path.getText(), node);
|
||||
}
|
||||
}
|
||||
_validateImport(path, node) {
|
||||
// remove quotes
|
||||
path = path.slice(1, -1);
|
||||
// resolve relative paths
|
||||
if (path[0] === '.') {
|
||||
path = path_1.join(this.getSourceFile().fileName, path);
|
||||
}
|
||||
if (/vs(\/|\\)nls/.test(path)) {
|
||||
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Not allowed to import vs/nls in standalone editor modules. Use standaloneStrings.ts`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as Lint from 'tslint';
|
||||
import { join } from 'path';
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
if (
|
||||
/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(sourceFile.fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(sourceFile.fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(sourceFile.fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(sourceFile.fileName)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(sourceFile.fileName)
|
||||
) {
|
||||
return this.applyWithWalker(new NoNlsInStandaloneEditorRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
class NoNlsInStandaloneEditorRuleWalker extends Lint.RuleWalker {
|
||||
|
||||
constructor(file: ts.SourceFile, opts: Lint.IOptions) {
|
||||
super(file, opts);
|
||||
}
|
||||
|
||||
protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void {
|
||||
if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) {
|
||||
this._validateImport(node.moduleReference.expression.getText(), node);
|
||||
}
|
||||
}
|
||||
|
||||
protected visitImportDeclaration(node: ts.ImportDeclaration): void {
|
||||
this._validateImport(node.moduleSpecifier.getText(), node);
|
||||
}
|
||||
|
||||
protected visitCallExpression(node: ts.CallExpression): void {
|
||||
super.visitCallExpression(node);
|
||||
|
||||
// import('foo') statements inside the code
|
||||
if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
||||
const [path] = node.arguments;
|
||||
this._validateImport(path.getText(), node);
|
||||
}
|
||||
}
|
||||
|
||||
private _validateImport(path: string, node: ts.Node): void {
|
||||
// remove quotes
|
||||
path = path.slice(1, -1);
|
||||
|
||||
// resolve relative paths
|
||||
if (path[0] === '.') {
|
||||
path = join(this.getSourceFile().fileName, path);
|
||||
}
|
||||
|
||||
if (
|
||||
/vs(\/|\\)nls/.test(path)
|
||||
) {
|
||||
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Not allowed to import vs/nls in standalone editor modules. Use standaloneStrings.ts`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const Lint = require("tslint");
|
||||
const minimatch = require("minimatch");
|
||||
const abstractGlobalsRule_1 = require("./abstractGlobalsRule");
|
||||
class Rule extends Lint.Rules.TypedRule {
|
||||
applyWithProgram(sourceFile, program) {
|
||||
const configs = this.getOptions().ruleArguments;
|
||||
for (const config of configs) {
|
||||
if (minimatch(sourceFile.fileName, config.target)) {
|
||||
return this.applyWithWalker(new NoNodejsGlobalsRuleWalker(sourceFile, program, this.getOptions(), config));
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
exports.Rule = Rule;
|
||||
class NoNodejsGlobalsRuleWalker extends abstractGlobalsRule_1.AbstractGlobalsRuleWalker {
|
||||
getDefinitionPattern() {
|
||||
return '@types/node';
|
||||
}
|
||||
getDisallowedGlobals() {
|
||||
// https://nodejs.org/api/globals.html#globals_global_objects
|
||||
return [
|
||||
"NodeJS",
|
||||
"Buffer",
|
||||
"__dirname",
|
||||
"__filename",
|
||||
"clearImmediate",
|
||||
"exports",
|
||||
"global",
|
||||
"module",
|
||||
"process",
|
||||
"setImmediate"
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as Lint from 'tslint';
|
||||
import * as minimatch from 'minimatch';
|
||||
import { AbstractGlobalsRuleWalker } from './abstractGlobalsRule';
|
||||
|
||||
interface NoNodejsGlobalsConfig {
|
||||
target: string;
|
||||
allowed: string[];
|
||||
}
|
||||
|
||||
export class Rule extends Lint.Rules.TypedRule {
|
||||
|
||||
applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
|
||||
const configs = <NoNodejsGlobalsConfig[]>this.getOptions().ruleArguments;
|
||||
|
||||
for (const config of configs) {
|
||||
if (minimatch(sourceFile.fileName, config.target)) {
|
||||
return this.applyWithWalker(new NoNodejsGlobalsRuleWalker(sourceFile, program, this.getOptions(), config));
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
class NoNodejsGlobalsRuleWalker extends AbstractGlobalsRuleWalker {
|
||||
|
||||
getDefinitionPattern(): string {
|
||||
return '@types/node';
|
||||
}
|
||||
|
||||
getDisallowedGlobals(): string[] {
|
||||
// https://nodejs.org/api/globals.html#globals_global_objects
|
||||
return [
|
||||
"NodeJS",
|
||||
"Buffer",
|
||||
"__dirname",
|
||||
"__filename",
|
||||
"clearImmediate",
|
||||
"exports",
|
||||
"global",
|
||||
"module",
|
||||
"process",
|
||||
"setImmediate"
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ts = require("typescript");
|
||||
const Lint = require("tslint");
|
||||
const path_1 = require("path");
|
||||
class Rule extends Lint.Rules.AbstractRule {
|
||||
apply(sourceFile) {
|
||||
if (/vs(\/|\\)editor/.test(sourceFile.fileName)) {
|
||||
// the vs/editor folder is allowed to use the standalone editor
|
||||
return [];
|
||||
}
|
||||
return this.applyWithWalker(new NoStandaloneEditorRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
}
|
||||
exports.Rule = Rule;
|
||||
class NoStandaloneEditorRuleWalker extends Lint.RuleWalker {
|
||||
constructor(file, opts) {
|
||||
super(file, opts);
|
||||
}
|
||||
visitImportEqualsDeclaration(node) {
|
||||
if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) {
|
||||
this._validateImport(node.moduleReference.expression.getText(), node);
|
||||
}
|
||||
}
|
||||
visitImportDeclaration(node) {
|
||||
this._validateImport(node.moduleSpecifier.getText(), node);
|
||||
}
|
||||
visitCallExpression(node) {
|
||||
super.visitCallExpression(node);
|
||||
// import('foo') statements inside the code
|
||||
if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
||||
const [path] = node.arguments;
|
||||
this._validateImport(path.getText(), node);
|
||||
}
|
||||
}
|
||||
// {{SQL CARBON EDIT}} - Rename node argument to _node to prevent errors since it is not used
|
||||
_validateImport(path, _node) {
|
||||
// remove quotes
|
||||
path = path.slice(1, -1);
|
||||
// resolve relative paths
|
||||
if (path[0] === '.') {
|
||||
path = path_1.join(this.getSourceFile().fileName, path);
|
||||
}
|
||||
if (/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(path)) {
|
||||
// {{SQL CARBON EDIT}}
|
||||
//this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Not allowed to import standalone editor modules. See https://github.com/Microsoft/vscode/wiki/Code-Organization`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as Lint from 'tslint';
|
||||
import { join } from 'path';
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
if (/vs(\/|\\)editor/.test(sourceFile.fileName)) {
|
||||
// the vs/editor folder is allowed to use the standalone editor
|
||||
return [];
|
||||
}
|
||||
return this.applyWithWalker(new NoStandaloneEditorRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
}
|
||||
|
||||
class NoStandaloneEditorRuleWalker extends Lint.RuleWalker {
|
||||
|
||||
constructor(file: ts.SourceFile, opts: Lint.IOptions) {
|
||||
super(file, opts);
|
||||
}
|
||||
|
||||
protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void {
|
||||
if (node.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) {
|
||||
this._validateImport(node.moduleReference.expression.getText(), node);
|
||||
}
|
||||
}
|
||||
|
||||
protected visitImportDeclaration(node: ts.ImportDeclaration): void {
|
||||
this._validateImport(node.moduleSpecifier.getText(), node);
|
||||
}
|
||||
|
||||
protected visitCallExpression(node: ts.CallExpression): void {
|
||||
super.visitCallExpression(node);
|
||||
|
||||
// import('foo') statements inside the code
|
||||
if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
||||
const [path] = node.arguments;
|
||||
this._validateImport(path.getText(), node);
|
||||
}
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}} - Rename node argument to _node to prevent errors since it is not used
|
||||
private _validateImport(path: string, _node: ts.Node): void {
|
||||
// remove quotes
|
||||
path = path.slice(1, -1);
|
||||
|
||||
// resolve relative paths
|
||||
if (path[0] === '.') {
|
||||
path = join(this.getSourceFile().fileName, path);
|
||||
}
|
||||
|
||||
if (
|
||||
/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(path)
|
||||
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(path)
|
||||
) {
|
||||
// {{SQL CARBON EDIT}}
|
||||
//this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Not allowed to import standalone editor modules. See https://github.com/Microsoft/vscode/wiki/Code-Organization`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const Lint = require("tslint");
|
||||
const minimatch = require("minimatch");
|
||||
class Rule extends Lint.Rules.AbstractRule {
|
||||
apply(sourceFile) {
|
||||
const args = this.getOptions().ruleArguments[0];
|
||||
if (args.exclude.every(x => !minimatch(sourceFile.fileName, x))) {
|
||||
return this.applyWithWalker(new NoSyncRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
exports.Rule = Rule;
|
||||
class NoSyncRuleWalker extends Lint.RuleWalker {
|
||||
constructor(file, opts) {
|
||||
super(file, opts);
|
||||
}
|
||||
visitCallExpression(node) {
|
||||
if (node.expression && NoSyncRuleWalker.operations.some(x => node.expression.getText().indexOf(x) >= 0)) {
|
||||
this.addFailureAtNode(node, `Do not use Sync operations`);
|
||||
}
|
||||
super.visitCallExpression(node);
|
||||
}
|
||||
}
|
||||
NoSyncRuleWalker.operations = ['readFileSync', 'writeFileSync', 'existsSync', 'fchmodSync', 'lchmodSync',
|
||||
'statSync', 'fstatSync', 'lstatSync', 'linkSync', 'symlinkSync', 'readlinkSync', 'realpathSync', 'unlinkSync', 'rmdirSync',
|
||||
'mkdirSync', 'mkdtempSync', 'readdirSync', 'openSync', 'utimesSync', 'futimesSync', 'fsyncSync', 'writeSync', 'readSync',
|
||||
'appendFileSync', 'accessSync', 'fdatasyncSync', 'copyFileSync'];
|
||||
@@ -1,45 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as Lint from 'tslint';
|
||||
import * as minimatch from 'minimatch';
|
||||
|
||||
interface NoSyncRuleConfig {
|
||||
exclude: string[];
|
||||
}
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
|
||||
apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
const args = <NoSyncRuleConfig>this.getOptions().ruleArguments[0];
|
||||
|
||||
if (args.exclude.every(x => !minimatch(sourceFile.fileName, x))) {
|
||||
return this.applyWithWalker(new NoSyncRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
class NoSyncRuleWalker extends Lint.RuleWalker {
|
||||
|
||||
private static readonly operations = ['readFileSync', 'writeFileSync', 'existsSync', 'fchmodSync', 'lchmodSync',
|
||||
'statSync', 'fstatSync', 'lstatSync', 'linkSync', 'symlinkSync', 'readlinkSync', 'realpathSync', 'unlinkSync', 'rmdirSync',
|
||||
'mkdirSync', 'mkdtempSync', 'readdirSync', 'openSync', 'utimesSync', 'futimesSync', 'fsyncSync', 'writeSync', 'readSync',
|
||||
'appendFileSync', 'accessSync', 'fdatasyncSync', 'copyFileSync'];
|
||||
|
||||
constructor(file: ts.SourceFile, opts: Lint.IOptions) {
|
||||
super(file, opts);
|
||||
}
|
||||
|
||||
visitCallExpression(node: ts.CallExpression) {
|
||||
if (node.expression && NoSyncRuleWalker.operations.some(x => node.expression.getText().indexOf(x) >= 0)) {
|
||||
this.addFailureAtNode(node, `Do not use Sync operations`);
|
||||
}
|
||||
|
||||
super.visitCallExpression(node);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ts = require("typescript");
|
||||
const Lint = require("tslint");
|
||||
/**
|
||||
* Implementation of the no-localize-keys-with-underscore rule which verifies that keys to the localize
|
||||
* calls don't contain underscores (_) since those break the localization process.
|
||||
*/
|
||||
class Rule extends Lint.Rules.AbstractRule {
|
||||
apply(sourceFile) {
|
||||
return this.applyWithWalker(new NoLocalizeKeysWithUnderscore(sourceFile, this.getOptions()));
|
||||
}
|
||||
}
|
||||
exports.Rule = Rule;
|
||||
const signatures = [
|
||||
"localize",
|
||||
"nls.localize"
|
||||
];
|
||||
class NoLocalizeKeysWithUnderscore extends Lint.RuleWalker {
|
||||
constructor(file, opts) {
|
||||
super(file, opts);
|
||||
}
|
||||
visitCallExpression(node) {
|
||||
this.checkCallExpression(node);
|
||||
super.visitCallExpression(node);
|
||||
}
|
||||
checkCallExpression(node) {
|
||||
// If this isn't one of the localize functions then continue on
|
||||
const functionName = node.expression.getText();
|
||||
if (functionName && !signatures.some(s => s === functionName)) {
|
||||
return;
|
||||
}
|
||||
const arg = node && node.arguments && node.arguments.length > 0 ? node.arguments[0] : undefined; // The key is the first element
|
||||
// Ignore if the arg isn't a string - we expect the compiler to warn if that's an issue
|
||||
if (arg && ts.isStringLiteral(arg)) {
|
||||
if (arg.getText().indexOf('_') >= 0) {
|
||||
const fix = [
|
||||
Lint.Replacement.replaceFromTo(arg.getStart(), arg.getEnd(), `${arg.getText().replace(/_/g, '.')}`),
|
||||
];
|
||||
this.addFailure(this.createFailure(arg.getStart(), arg.getWidth(), `Keys for localize calls must not contain underscores. Use periods (.) instead.`, fix));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as Lint from 'tslint';
|
||||
|
||||
/**
|
||||
* Implementation of the no-localize-keys-with-underscore rule which verifies that keys to the localize
|
||||
* calls don't contain underscores (_) since those break the localization process.
|
||||
*/
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
return this.applyWithWalker(new NoLocalizeKeysWithUnderscore(sourceFile, this.getOptions()));
|
||||
}
|
||||
}
|
||||
|
||||
const signatures = [
|
||||
"localize",
|
||||
"nls.localize"
|
||||
];
|
||||
|
||||
class NoLocalizeKeysWithUnderscore extends Lint.RuleWalker {
|
||||
|
||||
constructor(file: ts.SourceFile, opts: Lint.IOptions) {
|
||||
super(file, opts);
|
||||
}
|
||||
|
||||
protected visitCallExpression(node: ts.CallExpression): void {
|
||||
this.checkCallExpression(node);
|
||||
super.visitCallExpression(node);
|
||||
}
|
||||
|
||||
private checkCallExpression(node: ts.CallExpression): void {
|
||||
// If this isn't one of the localize functions then continue on
|
||||
const functionName = node.expression.getText();
|
||||
if (functionName && !signatures.some(s => s === functionName)) {
|
||||
return;
|
||||
}
|
||||
const arg = node && node.arguments && node.arguments.length > 0 ? node.arguments[0] : undefined; // The key is the first element
|
||||
// Ignore if the arg isn't a string - we expect the compiler to warn if that's an issue
|
||||
if(arg && ts.isStringLiteral(arg)) {
|
||||
if (arg.getText().indexOf('_') >= 0) {
|
||||
const fix = [
|
||||
Lint.Replacement.replaceFromTo(arg.getStart(), arg.getEnd(), `${arg.getText().replace(/_/g, '.')}`),
|
||||
];
|
||||
this.addFailure(this.createFailure(
|
||||
arg.getStart(), arg.getWidth(),
|
||||
`Keys for localize calls must not contain underscores. Use periods (.) instead.`, fix));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const ts = require("typescript");
|
||||
const Lint = require("tslint");
|
||||
/**
|
||||
* Implementation of the no-unexternalized-strings rule.
|
||||
*/
|
||||
class Rule extends Lint.Rules.AbstractRule {
|
||||
apply(sourceFile) {
|
||||
if (/\.d.ts$/.test(sourceFile.fileName)) {
|
||||
return [];
|
||||
}
|
||||
return this.applyWithWalker(new NoUnexternalizedStringsRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
}
|
||||
exports.Rule = Rule;
|
||||
function isStringLiteral(node) {
|
||||
return node && node.kind === ts.SyntaxKind.StringLiteral;
|
||||
}
|
||||
function isObjectLiteral(node) {
|
||||
return node && node.kind === ts.SyntaxKind.ObjectLiteralExpression;
|
||||
}
|
||||
function isPropertyAssignment(node) {
|
||||
return node && node.kind === ts.SyntaxKind.PropertyAssignment;
|
||||
}
|
||||
class NoUnexternalizedStringsRuleWalker extends Lint.RuleWalker {
|
||||
constructor(file, opts) {
|
||||
super(file, opts);
|
||||
this.signatures = Object.create(null);
|
||||
this.ignores = Object.create(null);
|
||||
this.messageIndex = undefined;
|
||||
this.keyIndex = undefined;
|
||||
this.usedKeys = Object.create(null);
|
||||
const options = this.getOptions();
|
||||
const first = options && options.length > 0 ? options[0] : null;
|
||||
if (first) {
|
||||
if (Array.isArray(first.signatures)) {
|
||||
first.signatures.forEach((signature) => this.signatures[signature] = true);
|
||||
}
|
||||
if (Array.isArray(first.ignores)) {
|
||||
first.ignores.forEach((ignore) => this.ignores[ignore] = true);
|
||||
}
|
||||
if (typeof first.messageIndex !== 'undefined') {
|
||||
this.messageIndex = first.messageIndex;
|
||||
}
|
||||
if (typeof first.keyIndex !== 'undefined') {
|
||||
this.keyIndex = first.keyIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
visitSourceFile(node) {
|
||||
super.visitSourceFile(node);
|
||||
Object.keys(this.usedKeys).forEach(key => {
|
||||
// Keys are quoted.
|
||||
let identifier = key.substr(1, key.length - 2);
|
||||
if (!NoUnexternalizedStringsRuleWalker.IDENTIFIER.test(identifier)) {
|
||||
let occurrence = this.usedKeys[key][0];
|
||||
this.addFailure(this.createFailure(occurrence.key.getStart(), occurrence.key.getWidth(), `The key ${occurrence.key.getText()} doesn't conform to a valid localize identifier`));
|
||||
}
|
||||
const occurrences = this.usedKeys[key];
|
||||
if (occurrences.length > 1) {
|
||||
occurrences.forEach(occurrence => {
|
||||
this.addFailure((this.createFailure(occurrence.key.getStart(), occurrence.key.getWidth(), `Duplicate key ${occurrence.key.getText()} with different message value.`)));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
visitStringLiteral(node) {
|
||||
this.checkStringLiteral(node);
|
||||
super.visitStringLiteral(node);
|
||||
}
|
||||
checkStringLiteral(node) {
|
||||
const text = node.getText();
|
||||
const doubleQuoted = text.length >= 2 && text[0] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE && text[text.length - 1] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE;
|
||||
const info = this.findDescribingParent(node);
|
||||
// Ignore strings in import and export nodes.
|
||||
if (info && info.isImport && doubleQuoted) {
|
||||
const fix = [
|
||||
Lint.Replacement.replaceFromTo(node.getStart(), 1, '\''),
|
||||
Lint.Replacement.replaceFromTo(node.getStart() + text.length - 1, 1, '\''),
|
||||
];
|
||||
this.addFailureAtNode(node, NoUnexternalizedStringsRuleWalker.ImportFailureMessage, fix);
|
||||
return;
|
||||
}
|
||||
const callInfo = info ? info.callInfo : null;
|
||||
const functionName = callInfo ? callInfo.callExpression.expression.getText() : null;
|
||||
if (functionName && this.ignores[functionName]) {
|
||||
return;
|
||||
}
|
||||
if (doubleQuoted && (!callInfo || callInfo.argIndex === -1 || !this.signatures[functionName])) {
|
||||
const s = node.getText();
|
||||
const fix = [
|
||||
Lint.Replacement.replaceFromTo(node.getStart(), node.getWidth(), `nls.localize('KEY-${s.substring(1, s.length - 1)}', ${s})`),
|
||||
];
|
||||
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Unexternalized string found: ${node.getText()}`, fix));
|
||||
return;
|
||||
}
|
||||
// We have a single quoted string outside a localize function name.
|
||||
if (!doubleQuoted && !this.signatures[functionName]) {
|
||||
return;
|
||||
}
|
||||
// We have a string that is a direct argument into the localize call.
|
||||
const keyArg = callInfo && callInfo.argIndex === this.keyIndex
|
||||
? callInfo.callExpression.arguments[this.keyIndex]
|
||||
: null;
|
||||
if (keyArg) {
|
||||
if (isStringLiteral(keyArg)) {
|
||||
this.recordKey(keyArg, this.messageIndex && callInfo ? callInfo.callExpression.arguments[this.messageIndex] : undefined);
|
||||
}
|
||||
else if (isObjectLiteral(keyArg)) {
|
||||
for (const property of keyArg.properties) {
|
||||
if (isPropertyAssignment(property)) {
|
||||
const name = property.name.getText();
|
||||
if (name === 'key') {
|
||||
const initializer = property.initializer;
|
||||
if (isStringLiteral(initializer)) {
|
||||
this.recordKey(initializer, this.messageIndex && callInfo ? callInfo.callExpression.arguments[this.messageIndex] : undefined);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const messageArg = callInfo.callExpression.arguments[this.messageIndex];
|
||||
if (messageArg && messageArg.kind !== ts.SyntaxKind.StringLiteral) {
|
||||
this.addFailure(this.createFailure(messageArg.getStart(), messageArg.getWidth(), `Message argument to '${callInfo.callExpression.expression.getText()}' must be a string literal.`));
|
||||
return;
|
||||
}
|
||||
}
|
||||
recordKey(keyNode, messageNode) {
|
||||
const text = keyNode.getText();
|
||||
// We have an empty key
|
||||
if (text.match(/(['"]) *\1/)) {
|
||||
if (messageNode) {
|
||||
this.addFailureAtNode(keyNode, `Key is empty for message: ${messageNode.getText()}`);
|
||||
}
|
||||
else {
|
||||
this.addFailureAtNode(keyNode, `Key is empty.`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
let occurrences = this.usedKeys[text];
|
||||
if (!occurrences) {
|
||||
occurrences = [];
|
||||
this.usedKeys[text] = occurrences;
|
||||
}
|
||||
if (messageNode) {
|
||||
if (occurrences.some(pair => pair.message ? pair.message.getText() === messageNode.getText() : false)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
occurrences.push({ key: keyNode, message: messageNode });
|
||||
}
|
||||
findDescribingParent(node) {
|
||||
let parent;
|
||||
while ((parent = node.parent)) {
|
||||
const kind = parent.kind;
|
||||
if (kind === ts.SyntaxKind.CallExpression) {
|
||||
const callExpression = parent;
|
||||
return { callInfo: { callExpression: callExpression, argIndex: callExpression.arguments.indexOf(node) } };
|
||||
}
|
||||
else if (kind === ts.SyntaxKind.ImportEqualsDeclaration || kind === ts.SyntaxKind.ImportDeclaration || kind === ts.SyntaxKind.ExportDeclaration) {
|
||||
return { isImport: true };
|
||||
}
|
||||
else if (kind === ts.SyntaxKind.VariableDeclaration || kind === ts.SyntaxKind.FunctionDeclaration || kind === ts.SyntaxKind.PropertyDeclaration
|
||||
|| kind === ts.SyntaxKind.MethodDeclaration || kind === ts.SyntaxKind.VariableDeclarationList || kind === ts.SyntaxKind.InterfaceDeclaration
|
||||
|| kind === ts.SyntaxKind.ClassDeclaration || kind === ts.SyntaxKind.EnumDeclaration || kind === ts.SyntaxKind.ModuleDeclaration
|
||||
|| kind === ts.SyntaxKind.TypeAliasDeclaration || kind === ts.SyntaxKind.SourceFile) {
|
||||
return null;
|
||||
}
|
||||
node = parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
NoUnexternalizedStringsRuleWalker.ImportFailureMessage = 'Do not use double quotes for imports.';
|
||||
NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE = '"';
|
||||
NoUnexternalizedStringsRuleWalker.IDENTIFIER = /^[_a-zA-Z0-9][ .\-_a-zA-Z0-9]*$/;
|
||||
@@ -1,222 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as Lint from 'tslint';
|
||||
|
||||
/**
|
||||
* Implementation of the no-unexternalized-strings rule.
|
||||
*/
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
if (/\.d.ts$/.test(sourceFile.fileName)) {
|
||||
return [];
|
||||
}
|
||||
return this.applyWithWalker(new NoUnexternalizedStringsRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
}
|
||||
|
||||
interface Map<V> {
|
||||
[key: string]: V;
|
||||
}
|
||||
|
||||
interface UnexternalizedStringsOptions {
|
||||
signatures?: string[];
|
||||
messageIndex?: number;
|
||||
keyIndex?: number;
|
||||
ignores?: string[];
|
||||
}
|
||||
|
||||
function isStringLiteral(node: ts.Node): node is ts.StringLiteral {
|
||||
return node && node.kind === ts.SyntaxKind.StringLiteral;
|
||||
}
|
||||
|
||||
function isObjectLiteral(node: ts.Node): node is ts.ObjectLiteralExpression {
|
||||
return node && node.kind === ts.SyntaxKind.ObjectLiteralExpression;
|
||||
}
|
||||
|
||||
function isPropertyAssignment(node: ts.Node): node is ts.PropertyAssignment {
|
||||
return node && node.kind === ts.SyntaxKind.PropertyAssignment;
|
||||
}
|
||||
|
||||
interface KeyMessagePair {
|
||||
key: ts.StringLiteral;
|
||||
message: ts.Node | undefined;
|
||||
}
|
||||
|
||||
class NoUnexternalizedStringsRuleWalker extends Lint.RuleWalker {
|
||||
|
||||
private static ImportFailureMessage = 'Do not use double quotes for imports.';
|
||||
|
||||
private static DOUBLE_QUOTE: string = '"';
|
||||
|
||||
private signatures: Map<boolean>;
|
||||
private messageIndex: number | undefined;
|
||||
private keyIndex: number | undefined;
|
||||
private ignores: Map<boolean>;
|
||||
|
||||
private usedKeys: Map<KeyMessagePair[]>;
|
||||
|
||||
constructor(file: ts.SourceFile, opts: Lint.IOptions) {
|
||||
super(file, opts);
|
||||
this.signatures = Object.create(null);
|
||||
this.ignores = Object.create(null);
|
||||
this.messageIndex = undefined;
|
||||
this.keyIndex = undefined;
|
||||
this.usedKeys = Object.create(null);
|
||||
const options: any[] = this.getOptions();
|
||||
const first: UnexternalizedStringsOptions = options && options.length > 0 ? options[0] : null;
|
||||
if (first) {
|
||||
if (Array.isArray(first.signatures)) {
|
||||
first.signatures.forEach((signature: string) => this.signatures[signature] = true);
|
||||
}
|
||||
if (Array.isArray(first.ignores)) {
|
||||
first.ignores.forEach((ignore: string) => this.ignores[ignore] = true);
|
||||
}
|
||||
if (typeof first.messageIndex !== 'undefined') {
|
||||
this.messageIndex = first.messageIndex;
|
||||
}
|
||||
if (typeof first.keyIndex !== 'undefined') {
|
||||
this.keyIndex = first.keyIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IDENTIFIER = /^[_a-zA-Z0-9][ .\-_a-zA-Z0-9]*$/;
|
||||
protected visitSourceFile(node: ts.SourceFile): void {
|
||||
super.visitSourceFile(node);
|
||||
Object.keys(this.usedKeys).forEach(key => {
|
||||
// Keys are quoted.
|
||||
let identifier = key.substr(1, key.length - 2);
|
||||
if (!NoUnexternalizedStringsRuleWalker.IDENTIFIER.test(identifier)) {
|
||||
let occurrence = this.usedKeys[key][0];
|
||||
this.addFailure(this.createFailure(occurrence.key.getStart(), occurrence.key.getWidth(), `The key ${occurrence.key.getText()} doesn't conform to a valid localize identifier`));
|
||||
}
|
||||
const occurrences = this.usedKeys[key];
|
||||
if (occurrences.length > 1) {
|
||||
occurrences.forEach(occurrence => {
|
||||
this.addFailure((this.createFailure(occurrence.key.getStart(), occurrence.key.getWidth(), `Duplicate key ${occurrence.key.getText()} with different message value.`)));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected visitStringLiteral(node: ts.StringLiteral): void {
|
||||
this.checkStringLiteral(node);
|
||||
super.visitStringLiteral(node);
|
||||
}
|
||||
|
||||
private checkStringLiteral(node: ts.StringLiteral): void {
|
||||
const text = node.getText();
|
||||
const doubleQuoted = text.length >= 2 && text[0] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE && text[text.length - 1] === NoUnexternalizedStringsRuleWalker.DOUBLE_QUOTE;
|
||||
const info = this.findDescribingParent(node);
|
||||
// Ignore strings in import and export nodes.
|
||||
if (info && info.isImport && doubleQuoted) {
|
||||
const fix = [
|
||||
Lint.Replacement.replaceFromTo(node.getStart(), 1, '\''),
|
||||
Lint.Replacement.replaceFromTo(node.getStart() + text.length - 1, 1, '\''),
|
||||
];
|
||||
this.addFailureAtNode(
|
||||
node,
|
||||
NoUnexternalizedStringsRuleWalker.ImportFailureMessage,
|
||||
fix
|
||||
);
|
||||
return;
|
||||
}
|
||||
const callInfo = info ? info.callInfo : null;
|
||||
const functionName = callInfo ? callInfo.callExpression.expression.getText() : null;
|
||||
if (functionName && this.ignores[functionName]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (doubleQuoted && (!callInfo || callInfo.argIndex === -1 || !this.signatures[functionName!])) {
|
||||
const s = node.getText();
|
||||
const fix = [
|
||||
Lint.Replacement.replaceFromTo(node.getStart(), node.getWidth(), `nls.localize('KEY-${s.substring(1, s.length - 1)}', ${s})`),
|
||||
];
|
||||
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), `Unexternalized string found: ${node.getText()}`, fix));
|
||||
return;
|
||||
}
|
||||
// We have a single quoted string outside a localize function name.
|
||||
if (!doubleQuoted && !this.signatures[functionName!]) {
|
||||
return;
|
||||
}
|
||||
// We have a string that is a direct argument into the localize call.
|
||||
const keyArg: ts.Expression | null = callInfo && callInfo.argIndex === this.keyIndex
|
||||
? callInfo.callExpression.arguments[this.keyIndex]
|
||||
: null;
|
||||
if (keyArg) {
|
||||
if (isStringLiteral(keyArg)) {
|
||||
this.recordKey(keyArg, this.messageIndex && callInfo ? callInfo.callExpression.arguments[this.messageIndex] : undefined);
|
||||
} else if (isObjectLiteral(keyArg)) {
|
||||
for (const property of keyArg.properties) {
|
||||
if (isPropertyAssignment(property)) {
|
||||
const name = property.name.getText();
|
||||
if (name === 'key') {
|
||||
const initializer = property.initializer;
|
||||
if (isStringLiteral(initializer)) {
|
||||
this.recordKey(initializer, this.messageIndex && callInfo ? callInfo.callExpression.arguments[this.messageIndex] : undefined);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const messageArg = callInfo!.callExpression.arguments[this.messageIndex!];
|
||||
|
||||
if (messageArg && messageArg.kind !== ts.SyntaxKind.StringLiteral) {
|
||||
this.addFailure(this.createFailure(
|
||||
messageArg.getStart(), messageArg.getWidth(),
|
||||
`Message argument to '${callInfo!.callExpression.expression.getText()}' must be a string literal.`));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private recordKey(keyNode: ts.StringLiteral, messageNode: ts.Node | undefined) {
|
||||
const text = keyNode.getText();
|
||||
// We have an empty key
|
||||
if (text.match(/(['"]) *\1/)) {
|
||||
if (messageNode) {
|
||||
this.addFailureAtNode(keyNode, `Key is empty for message: ${messageNode.getText()}`);
|
||||
} else {
|
||||
this.addFailureAtNode(keyNode, `Key is empty.`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
let occurrences: KeyMessagePair[] = this.usedKeys[text];
|
||||
if (!occurrences) {
|
||||
occurrences = [];
|
||||
this.usedKeys[text] = occurrences;
|
||||
}
|
||||
if (messageNode) {
|
||||
if (occurrences.some(pair => pair.message ? pair.message.getText() === messageNode.getText() : false)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
occurrences.push({ key: keyNode, message: messageNode });
|
||||
}
|
||||
|
||||
private findDescribingParent(node: ts.Node): { callInfo?: { callExpression: ts.CallExpression, argIndex: number }, isImport?: boolean; } | null {
|
||||
let parent: ts.Node;
|
||||
while ((parent = node.parent)) {
|
||||
const kind = parent.kind;
|
||||
if (kind === ts.SyntaxKind.CallExpression) {
|
||||
const callExpression = parent as ts.CallExpression;
|
||||
return { callInfo: { callExpression: callExpression, argIndex: callExpression.arguments.indexOf(<any>node) } };
|
||||
} else if (kind === ts.SyntaxKind.ImportEqualsDeclaration || kind === ts.SyntaxKind.ImportDeclaration || kind === ts.SyntaxKind.ExportDeclaration) {
|
||||
return { isImport: true };
|
||||
} else if (kind === ts.SyntaxKind.VariableDeclaration || kind === ts.SyntaxKind.FunctionDeclaration || kind === ts.SyntaxKind.PropertyDeclaration
|
||||
|| kind === ts.SyntaxKind.MethodDeclaration || kind === ts.SyntaxKind.VariableDeclarationList || kind === ts.SyntaxKind.InterfaceDeclaration
|
||||
|| kind === ts.SyntaxKind.ClassDeclaration || kind === ts.SyntaxKind.EnumDeclaration || kind === ts.SyntaxKind.ModuleDeclaration
|
||||
|| kind === ts.SyntaxKind.TypeAliasDeclaration || kind === ts.SyntaxKind.SourceFile) {
|
||||
return null;
|
||||
}
|
||||
node = parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const Lint = require("tslint");
|
||||
class Rule extends Lint.Rules.TypedRule {
|
||||
applyWithProgram(sourceFile, program) {
|
||||
if (program.getCompilerOptions().alwaysStrict) {
|
||||
return this.applyWithWalker(new NoUselessStrictRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
exports.Rule = Rule;
|
||||
class NoUselessStrictRuleWalker extends Lint.RuleWalker {
|
||||
visitStringLiteral(node) {
|
||||
this.checkStringLiteral(node);
|
||||
super.visitStringLiteral(node);
|
||||
}
|
||||
checkStringLiteral(node) {
|
||||
const text = node.getText();
|
||||
if (text === '\'use strict\'' || text === '"use strict"') {
|
||||
this.addFailureAtNode(node, 'use strict directive is unnecessary');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as Lint from 'tslint';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
export class Rule extends Lint.Rules.TypedRule {
|
||||
public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
|
||||
if (program.getCompilerOptions().alwaysStrict) {
|
||||
return this.applyWithWalker(new NoUselessStrictRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
class NoUselessStrictRuleWalker extends Lint.RuleWalker {
|
||||
protected visitStringLiteral(node: ts.StringLiteral): void {
|
||||
this.checkStringLiteral(node);
|
||||
super.visitStringLiteral(node);
|
||||
}
|
||||
|
||||
private checkStringLiteral(node: ts.StringLiteral): void {
|
||||
const text = node.getText();
|
||||
if (text === '\'use strict\'' || text === '"use strict"') {
|
||||
this.addFailureAtNode(node, 'use strict directive is unnecessary');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
"use strict";
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const Lint = require("tslint");
|
||||
const fs = require("fs");
|
||||
class Rule extends Lint.Rules.AbstractRule {
|
||||
apply(sourceFile) {
|
||||
return this.applyWithWalker(new TranslationRemindRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
}
|
||||
exports.Rule = Rule;
|
||||
class TranslationRemindRuleWalker extends Lint.RuleWalker {
|
||||
constructor(file, opts) {
|
||||
super(file, opts);
|
||||
}
|
||||
visitImportDeclaration(node) {
|
||||
const declaration = node.moduleSpecifier.getText();
|
||||
if (declaration !== `'${TranslationRemindRuleWalker.NLS_MODULE}'`) {
|
||||
return;
|
||||
}
|
||||
this.visitImportLikeDeclaration(node);
|
||||
}
|
||||
visitImportEqualsDeclaration(node) {
|
||||
const reference = node.moduleReference.getText();
|
||||
if (reference !== `require('${TranslationRemindRuleWalker.NLS_MODULE}')`) {
|
||||
return;
|
||||
}
|
||||
this.visitImportLikeDeclaration(node);
|
||||
}
|
||||
visitImportLikeDeclaration(node) {
|
||||
const currentFile = node.getSourceFile().fileName;
|
||||
const matchService = currentFile.match(/vs\/workbench\/services\/\w+/);
|
||||
const matchPart = currentFile.match(/vs\/workbench\/contrib\/\w+/);
|
||||
if (!matchService && !matchPart) {
|
||||
return;
|
||||
}
|
||||
const resource = matchService ? matchService[0] : matchPart[0];
|
||||
let resourceDefined = false;
|
||||
let json;
|
||||
try {
|
||||
json = fs.readFileSync('./build/lib/i18n.resources.json', 'utf8');
|
||||
}
|
||||
catch (e) {
|
||||
console.error('[translation-remind rule]: File with resources to pull from Transifex was not found. Aborting translation resource check for newly defined workbench part/service.');
|
||||
return;
|
||||
}
|
||||
const workbenchResources = JSON.parse(json).workbench;
|
||||
workbenchResources.forEach((existingResource) => {
|
||||
if (existingResource.name === resource) {
|
||||
resourceDefined = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
if (!resourceDefined) {
|
||||
this.addFailureAtNode(node, `Please add '${resource}' to ./build/lib/i18n.resources.json file to use translations here.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
TranslationRemindRuleWalker.NLS_MODULE = 'vs/nls';
|
||||
@@ -1,73 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
import * as Lint from 'tslint';
|
||||
import * as fs from 'fs';
|
||||
|
||||
export class Rule extends Lint.Rules.AbstractRule {
|
||||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
|
||||
return this.applyWithWalker(new TranslationRemindRuleWalker(sourceFile, this.getOptions()));
|
||||
}
|
||||
}
|
||||
|
||||
class TranslationRemindRuleWalker extends Lint.RuleWalker {
|
||||
|
||||
private static NLS_MODULE: string = 'vs/nls';
|
||||
|
||||
constructor(file: ts.SourceFile, opts: Lint.IOptions) {
|
||||
super(file, opts);
|
||||
}
|
||||
|
||||
protected visitImportDeclaration(node: ts.ImportDeclaration): void {
|
||||
const declaration = node.moduleSpecifier.getText();
|
||||
if (declaration !== `'${TranslationRemindRuleWalker.NLS_MODULE}'`) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.visitImportLikeDeclaration(node);
|
||||
}
|
||||
|
||||
protected visitImportEqualsDeclaration(node: ts.ImportEqualsDeclaration): void {
|
||||
const reference = node.moduleReference.getText();
|
||||
if (reference !== `require('${TranslationRemindRuleWalker.NLS_MODULE}')`) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.visitImportLikeDeclaration(node);
|
||||
}
|
||||
|
||||
private visitImportLikeDeclaration(node: ts.ImportDeclaration | ts.ImportEqualsDeclaration) {
|
||||
const currentFile = node.getSourceFile().fileName;
|
||||
const matchService = currentFile.match(/vs\/workbench\/services\/\w+/);
|
||||
const matchPart = currentFile.match(/vs\/workbench\/contrib\/\w+/);
|
||||
if (!matchService && !matchPart) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resource = matchService ? matchService[0] : matchPart![0];
|
||||
let resourceDefined = false;
|
||||
|
||||
let json;
|
||||
try {
|
||||
json = fs.readFileSync('./build/lib/i18n.resources.json', 'utf8');
|
||||
} catch (e) {
|
||||
console.error('[translation-remind rule]: File with resources to pull from Transifex was not found. Aborting translation resource check for newly defined workbench part/service.');
|
||||
return;
|
||||
}
|
||||
const workbenchResources = JSON.parse(json).workbench;
|
||||
|
||||
workbenchResources.forEach((existingResource: any) => {
|
||||
if (existingResource.name === resource) {
|
||||
resourceDefined = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (!resourceDefined) {
|
||||
this.addFailureAtNode(node, `Please add '${resource}' to ./build/lib/i18n.resources.json file to use translations here.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user