mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Custom ESLint rules to enforce new ThemedComponent selector convention
The following cases are covered: - ThemedComponent wrapper selectors must not start with ds-themed- - Base component selectors must start with ds-base- - Themed component selectors must start with ds-themed- - The ThemedComponent wrapper must always be used in HTML - The ThemedComponent wrapper must be used in TypeScript _where appropriate_: - Required - Explicit usages (e.g. modal instantiation, routing modules, ...) - By.css selector queries (in order to align with the HTML rule) - Unchecked - Non-routing modules (to ensure the components can be declared) - ViewChild hooks (since they need to attach to the underlying component) All rules work with --fix to automatically migrate to the new convention This covers most of the codebase, but minor manual adjustment are needed afterwards
This commit is contained in:
@@ -11,7 +11,10 @@
|
|||||||
"eslint-plugin-jsonc",
|
"eslint-plugin-jsonc",
|
||||||
"eslint-plugin-rxjs",
|
"eslint-plugin-rxjs",
|
||||||
"eslint-plugin-simple-import-sort",
|
"eslint-plugin-simple-import-sort",
|
||||||
"eslint-plugin-import-newlines"
|
"eslint-plugin-import-newlines",
|
||||||
|
"eslint-plugin-jsonc",
|
||||||
|
"dspace-angular-ts",
|
||||||
|
"dspace-angular-html"
|
||||||
],
|
],
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
@@ -238,7 +241,11 @@
|
|||||||
"method"
|
"method"
|
||||||
],
|
],
|
||||||
|
|
||||||
"rxjs/no-nested-subscribe": "off" // todo: go over _all_ cases
|
"rxjs/no-nested-subscribe": "off", // todo: go over _all_ cases
|
||||||
|
|
||||||
|
// Custom DSpace Angular rules
|
||||||
|
"dspace-angular-ts/themed-component-selectors": "error",
|
||||||
|
"dspace-angular-ts/themed-component-usages": "error"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -253,7 +260,10 @@
|
|||||||
"createDefaultProgram": true
|
"createDefaultProgram": true
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"prefer-const": "off"
|
"prefer-const": "off",
|
||||||
|
|
||||||
|
// Custom DSpace Angular rules
|
||||||
|
"dspace-angular-ts/themed-component-usages": "error"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -262,7 +272,11 @@
|
|||||||
],
|
],
|
||||||
"extends": [
|
"extends": [
|
||||||
"plugin:@angular-eslint/template/recommended"
|
"plugin:@angular-eslint/template/recommended"
|
||||||
]
|
],
|
||||||
|
"rules": {
|
||||||
|
// Custom DSpace Angular rules
|
||||||
|
"dspace-angular-html/themed-component-usages": "error"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"files": [
|
"files": [
|
||||||
|
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@@ -85,8 +85,14 @@ jobs:
|
|||||||
- name: Install Yarn dependencies
|
- name: Install Yarn dependencies
|
||||||
run: yarn install --frozen-lockfile
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build lint plugins
|
||||||
|
run: yarn run build:lint
|
||||||
|
|
||||||
|
- name: Run lint plugin tests
|
||||||
|
run: yarn run test:lint:nobuild
|
||||||
|
|
||||||
- name: Run lint
|
- name: Run lint
|
||||||
run: yarn run lint --quiet
|
run: yarn run lint:nobuild --quiet
|
||||||
|
|
||||||
- name: Check for circular dependencies
|
- name: Check for circular dependencies
|
||||||
run: yarn run check-circ-deps
|
run: yarn run check-circ-deps
|
||||||
|
4
lint/.gitignore
vendored
Normal file
4
lint/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/dist/
|
||||||
|
/coverage/
|
||||||
|
/node-modules/
|
||||||
|
/docs/
|
31
lint/README.md
Normal file
31
lint/README.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# ESLint plugins
|
||||||
|
|
||||||
|
Custom ESLint rules for DSpace Angular peculiarities.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
- Different file types must be handled by separate plugins. We support:
|
||||||
|
- [TypeScript](./src/ts)
|
||||||
|
- [HTML](./src/html)
|
||||||
|
- All rules are written in TypeScript and compiled into [`dist`](./dist)
|
||||||
|
- The plugins are linked into the main project dependencies from here
|
||||||
|
- These directories already contain the necessary `package.json` files to mark them as ESLint plugins
|
||||||
|
- The plugins are declared in [`.eslintrc.json`](../.eslintrc.json). Individual rules can be configured or disabled there, like usual.
|
||||||
|
- Some useful links
|
||||||
|
- [Developing ESLint plugins](https://eslint.org/docs/latest/extend/plugins)
|
||||||
|
- [Custom rules in typescript-eslint](https://typescript-eslint.io/developers/custom-rules)
|
||||||
|
- [Angular ESLint](https://github.com/angular-eslint/angular-eslint)
|
||||||
|
|
||||||
|
## Parsing project metadata in advance ~ TypeScript AST
|
||||||
|
|
||||||
|
While it is possible to retain persistent state between files during the linting process, it becomes quite complicated if the content of one file determines how we want to lint another file.
|
||||||
|
Because the two files may be linted out of order, we may not know whether the first file is wrong before we pass by the second. This means that we cannot report or fix the issue, because the first file is already detached from the linting context.
|
||||||
|
|
||||||
|
For example, we cannot consistently determine which components are themeable (i.e. have a `ThemedComponent` wrapper) while linting.
|
||||||
|
To work around this issue, we construct a registry of themeable components _before_ linting anything.
|
||||||
|
- We don't have a good way to hook into the ESLint parser at this time
|
||||||
|
- Instead, we leverage the actual TypeScript AST parser
|
||||||
|
- Retrieve all `ThemedComponent` wrapper files by the pattern of their path (`themed-*.component.ts`)
|
||||||
|
- Determine the themed component they're linked to (by the actual type annotation/import path, since filenames are prone to errors)
|
||||||
|
- Store metadata describing these component pairs in a global registry that can be shared between rules
|
||||||
|
- This only needs to happen once, and only takes a fraction of a second (for ~100 themeable components)
|
6
lint/dist/src/rules/html/package.json
vendored
Normal file
6
lint/dist/src/rules/html/package.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "eslint-plugin-dspace-angular-html",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"main": "./index.js",
|
||||||
|
"private": true
|
||||||
|
}
|
6
lint/dist/src/rules/ts/package.json
vendored
Normal file
6
lint/dist/src/rules/ts/package.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "eslint-plugin-dspace-angular-ts",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"main": "./index.js",
|
||||||
|
"private": true
|
||||||
|
}
|
7
lint/jasmine.json
Normal file
7
lint/jasmine.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"spec_files": ["**/*.spec.js"],
|
||||||
|
"spec_dir": "lint/dist/test",
|
||||||
|
"helpers": [
|
||||||
|
"./test/helpers.js"
|
||||||
|
]
|
||||||
|
}
|
16
lint/src/rules/html/index.ts
Normal file
16
lint/src/rules/html/index.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import themedComponentUsages from './themed-component-usages';
|
||||||
|
|
||||||
|
export = {
|
||||||
|
rules: {
|
||||||
|
'themed-component-usages': themedComponentUsages,
|
||||||
|
},
|
||||||
|
parser: require('@angular-eslint/template-parser'),
|
||||||
|
};
|
56
lint/src/rules/html/themed-component-usages.ts
Normal file
56
lint/src/rules/html/themed-component-usages.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
DISALLOWED_THEME_SELECTORS,
|
||||||
|
fixSelectors,
|
||||||
|
} from '../../util/theme-support';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
meta: {
|
||||||
|
type: 'problem',
|
||||||
|
fixable: 'code',
|
||||||
|
schema: [],
|
||||||
|
messages: {
|
||||||
|
mustUseThemedWrapperSelector: 'Themeable components should be used via their ThemedComponent wrapper\'s selector',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
create(context: any) {
|
||||||
|
return {
|
||||||
|
[`Element$1[name = /^${DISALLOWED_THEME_SELECTORS}/]`](node: any) {
|
||||||
|
context.report({
|
||||||
|
messageId: 'mustUseThemedWrapperSelector',
|
||||||
|
node,
|
||||||
|
fix(fixer: any) {
|
||||||
|
const oldSelector = node.name;
|
||||||
|
const newSelector = fixSelectors(oldSelector);
|
||||||
|
|
||||||
|
const openTagRange = [
|
||||||
|
node.startSourceSpan.start.offset + 1,
|
||||||
|
node.startSourceSpan.start.offset + 1 + oldSelector.length
|
||||||
|
];
|
||||||
|
|
||||||
|
const ops = [
|
||||||
|
fixer.replaceTextRange(openTagRange, newSelector),
|
||||||
|
];
|
||||||
|
|
||||||
|
// make sure we don't mangle self-closing tags
|
||||||
|
if (node.startSourceSpan.end.offset !== node.endSourceSpan.end.offset) {
|
||||||
|
const closeTagRange = [
|
||||||
|
node.endSourceSpan.start.offset + 2,
|
||||||
|
node.endSourceSpan.end.offset - 1
|
||||||
|
];
|
||||||
|
ops.push(fixer.replaceTextRange(closeTagRange, newSelector));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ops;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
9
lint/src/rules/ts/index.ts
Normal file
9
lint/src/rules/ts/index.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import themedComponentSelectors from './themed-component-selectors';
|
||||||
|
import themedComponentUsages from './themed-component-usages';
|
||||||
|
|
||||||
|
export = {
|
||||||
|
rules: {
|
||||||
|
'themed-component-selectors': themedComponentSelectors,
|
||||||
|
'themed-component-usages': themedComponentUsages,
|
||||||
|
},
|
||||||
|
};
|
92
lint/src/rules/ts/themed-component-selectors.ts
Normal file
92
lint/src/rules/ts/themed-component-selectors.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
import { ESLintUtils } from '@typescript-eslint/utils';
|
||||||
|
import { getComponentSelectorNode } from '../../util/angular';
|
||||||
|
import { stringLiteral } from '../../util/misc';
|
||||||
|
import {
|
||||||
|
inThemedComponentOverrideFile,
|
||||||
|
isThemeableComponent,
|
||||||
|
isThemedComponentWrapper,
|
||||||
|
} from '../../util/theme-support';
|
||||||
|
|
||||||
|
export default ESLintUtils.RuleCreator.withoutDocs({
|
||||||
|
meta: {
|
||||||
|
type: 'problem',
|
||||||
|
schema: [],
|
||||||
|
fixable: 'code',
|
||||||
|
messages: {
|
||||||
|
wrongSelectorUnthemedComponent: 'Unthemed version of themeable components should have a selector starting with \'ds-base-\'',
|
||||||
|
wrongSelectorThemedComponentWrapper: 'Themed component wrapper of themeable components shouldn\'t have a selector starting with \'ds-themed-\'',
|
||||||
|
wrongSelectorThemedComponentOverride: 'Theme override of themeable component should have a selector starting with \'ds-themed-\'',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultOptions: [],
|
||||||
|
create(context: any): any {
|
||||||
|
if (context.getFilename()?.endsWith('.spec.ts')) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function enforceWrapperSelector(selectorNode: any) {
|
||||||
|
if (selectorNode?.value.startsWith('ds-themed-')) {
|
||||||
|
context.report({
|
||||||
|
messageId: 'wrongSelectorThemedComponentWrapper',
|
||||||
|
node: selectorNode,
|
||||||
|
fix(fixer: any) {
|
||||||
|
return fixer.replaceText(selectorNode, stringLiteral(selectorNode.value.replace('ds-themed-', 'ds-')));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function enforceBaseSelector(selectorNode: any) {
|
||||||
|
if (!selectorNode?.value.startsWith('ds-base-')) {
|
||||||
|
context.report({
|
||||||
|
messageId: 'wrongSelectorUnthemedComponent',
|
||||||
|
node: selectorNode,
|
||||||
|
fix(fixer: any) {
|
||||||
|
return fixer.replaceText(selectorNode, stringLiteral(selectorNode.value.replace('ds-', 'ds-base-')));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function enforceThemedSelector(selectorNode: any) {
|
||||||
|
if (!selectorNode?.value.startsWith('ds-themed-')) {
|
||||||
|
context.report({
|
||||||
|
messageId: 'wrongSelectorThemedComponentOverride',
|
||||||
|
node: selectorNode,
|
||||||
|
fix(fixer: any) {
|
||||||
|
return fixer.replaceText(selectorNode, stringLiteral(selectorNode.value.replace('ds-', 'ds-themed-')));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'ClassDeclaration > Decorator[expression.callee.name = "Component"]'(node: any) {
|
||||||
|
// keep track of all @Component nodes by their selector
|
||||||
|
const selectorNode = getComponentSelectorNode(node);
|
||||||
|
const selector = selectorNode?.value;
|
||||||
|
const classNode = node.parent;
|
||||||
|
const className = classNode.id?.name;
|
||||||
|
|
||||||
|
if (selector === undefined || className === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isThemedComponentWrapper(node)) {
|
||||||
|
enforceWrapperSelector(selectorNode);
|
||||||
|
} else if (inThemedComponentOverrideFile(context)) {
|
||||||
|
enforceThemedSelector(selectorNode);
|
||||||
|
} else if (isThemeableComponent(className)) {
|
||||||
|
enforceBaseSelector(selectorNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
132
lint/src/rules/ts/themed-component-usages.ts
Normal file
132
lint/src/rules/ts/themed-component-usages.ts
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
import { ESLintUtils } from '@typescript-eslint/utils';
|
||||||
|
import { findUsages } from '../../util/misc';
|
||||||
|
import {
|
||||||
|
allThemeableComponents,
|
||||||
|
DISALLOWED_THEME_SELECTORS,
|
||||||
|
fixSelectors,
|
||||||
|
getThemeableComponentByBaseClass,
|
||||||
|
inThemedComponentFile,
|
||||||
|
isAllowedUnthemedUsage,
|
||||||
|
} from '../../util/theme-support';
|
||||||
|
|
||||||
|
export default ESLintUtils.RuleCreator.withoutDocs({
|
||||||
|
meta: {
|
||||||
|
type: 'problem',
|
||||||
|
schema: [],
|
||||||
|
fixable: 'code',
|
||||||
|
messages: {
|
||||||
|
mustUseThemedWrapper: 'Themeable components should be used via their ThemedComponent wrapper',
|
||||||
|
mustImportThemedWrapper: 'Themeable components should be used via their ThemedComponent wrapper',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultOptions: [],
|
||||||
|
create(context: any, options: any): any {
|
||||||
|
function handleUnthemedUsagesInTypescript(node: any) {
|
||||||
|
if (isAllowedUnthemedUsage(node)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entry = getThemeableComponentByBaseClass(node.name);
|
||||||
|
|
||||||
|
if (entry === undefined) {
|
||||||
|
// this should never happen
|
||||||
|
throw new Error(`No such themeable component in registry: '${node.name}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.report({
|
||||||
|
messageId: 'mustUseThemedWrapper',
|
||||||
|
node: node,
|
||||||
|
fix(fixer: any) {
|
||||||
|
return fixer.replaceText(node, entry.wrapperClass);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleThemedSelectorQueriesInTests(node: any) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUnthemedImportsInTypescript(specifierNode: any) {
|
||||||
|
const allUsages = findUsages(context, specifierNode.local);
|
||||||
|
const badUsages = allUsages.filter(usage => !isAllowedUnthemedUsage(usage));
|
||||||
|
|
||||||
|
if (badUsages.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const importedNode = specifierNode.imported;
|
||||||
|
const declarationNode = specifierNode.parent;
|
||||||
|
|
||||||
|
const entry = getThemeableComponentByBaseClass(importedNode.name);
|
||||||
|
if (entry === undefined) {
|
||||||
|
// this should never happen
|
||||||
|
throw new Error(`No such themeable component in registry: '${importedNode.name}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.report({
|
||||||
|
messageId: 'mustImportThemedWrapper',
|
||||||
|
node: importedNode,
|
||||||
|
fix(fixer: any) {
|
||||||
|
const ops = [];
|
||||||
|
|
||||||
|
const oldImportSource = declarationNode.source.value;
|
||||||
|
const newImportLine = `import { ${entry.wrapperClass} } from '${oldImportSource.replace(entry.baseFileName, entry.wrapperFileName)}';`;
|
||||||
|
|
||||||
|
if (declarationNode.specifiers.length === 1) {
|
||||||
|
if (allUsages.length === badUsages.length) {
|
||||||
|
ops.push(fixer.replaceText(declarationNode, newImportLine));
|
||||||
|
} else {
|
||||||
|
ops.push(fixer.insertTextAfter(declarationNode, newImportLine));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ops.push(fixer.replaceText(specifierNode, entry.wrapperClass));
|
||||||
|
ops.push(fixer.insertTextAfter(declarationNode, newImportLine));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ops;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore tests and non-routing modules
|
||||||
|
if (context.getFilename()?.endsWith('.spec.ts')) {
|
||||||
|
return {
|
||||||
|
[`CallExpression[callee.object.name = "By"][callee.property.name = "css"] > Literal[value = /.*${DISALLOWED_THEME_SELECTORS}.*/]`](node: any) {
|
||||||
|
context.report({
|
||||||
|
node,
|
||||||
|
messageId: 'mustUseThemedWrapper',
|
||||||
|
fix(fixer: any){
|
||||||
|
const newSelector = fixSelectors(node.raw);
|
||||||
|
return fixer.replaceText(node, newSelector);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else if (
|
||||||
|
context.getFilename()?.match(/(?!routing).module.ts$/)
|
||||||
|
|| context.getFilename()?.match(/themed-.+\.component\.ts$/)
|
||||||
|
|| inThemedComponentFile(context)
|
||||||
|
) {
|
||||||
|
// do nothing
|
||||||
|
return {};
|
||||||
|
} else {
|
||||||
|
return allThemeableComponents().reduce(
|
||||||
|
(rules, entry) => {
|
||||||
|
return {
|
||||||
|
...rules,
|
||||||
|
[`:not(:matches(ClassDeclaration, ImportSpecifier)) > Identifier[name = "${entry.baseClass}"]`]: handleUnthemedUsagesInTypescript,
|
||||||
|
[`ImportSpecifier[imported.name = "${entry.baseClass}"]`]: handleUnthemedImportsInTypescript,
|
||||||
|
};
|
||||||
|
}, {},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
16
lint/src/util/angular.ts
Normal file
16
lint/src/util/angular.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function getComponentSelectorNode(componentDecoratorNode: any): any | undefined {
|
||||||
|
for (const property of componentDecoratorNode.expression.arguments[0].properties) {
|
||||||
|
if (property.key?.name === 'selector') {
|
||||||
|
return property?.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
42
lint/src/util/misc.ts
Normal file
42
lint/src/util/misc.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function stringLiteral(value: string): string {
|
||||||
|
return `'${value}'`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function match(rangeA: number[], rangeB: number[]) {
|
||||||
|
return rangeA[0] === rangeB[0] && rangeA[1] === rangeB[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findUsages(context: any, localNode: any): any[] {
|
||||||
|
const ast = context.getSourceCode().ast;
|
||||||
|
|
||||||
|
const usages: any[] = [];
|
||||||
|
|
||||||
|
for (const token of ast.tokens) {
|
||||||
|
if (token.type === 'Identifier' && token.value === localNode.name && !match(token.range, localNode.range)) {
|
||||||
|
usages.push(context.getSourceCode().getNodeByRangeIndex(token.range[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return usages;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function isPartOfTypeExpression(node: any): boolean {
|
||||||
|
return node.parent.type.startsWith('TSType');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isClassDeclaration(node: any): boolean {
|
||||||
|
return node.parent.type === 'ClassDeclaration';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPartOfViewChild(node: any): boolean {
|
||||||
|
return node.parent?.callee?.name === 'ViewChild';
|
||||||
|
}
|
192
lint/src/util/theme-support.ts
Normal file
192
lint/src/util/theme-support.ts
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { readFileSync } from 'fs';
|
||||||
|
import { basename } from 'path';
|
||||||
|
import ts from 'typescript';
|
||||||
|
import {
|
||||||
|
isClassDeclaration,
|
||||||
|
isPartOfTypeExpression,
|
||||||
|
isPartOfViewChild,
|
||||||
|
} from './misc';
|
||||||
|
|
||||||
|
const glob = require('glob');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Couples a themeable Component to its ThemedComponent wrapper
|
||||||
|
*/
|
||||||
|
export interface ThemeableComponentRegistryEntry {
|
||||||
|
basePath: string;
|
||||||
|
baseFileName: string,
|
||||||
|
baseClass: string;
|
||||||
|
|
||||||
|
wrapperPath: string;
|
||||||
|
wrapperFileName: string,
|
||||||
|
wrapperClass: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listing of all themeable Components
|
||||||
|
*/
|
||||||
|
class ThemeableComponentRegistry {
|
||||||
|
public readonly entries: Set<ThemeableComponentRegistryEntry>;
|
||||||
|
public readonly byBaseClass: Map<string, ThemeableComponentRegistryEntry>;
|
||||||
|
public readonly byBasePath: Map<string, ThemeableComponentRegistryEntry>;
|
||||||
|
public readonly byWrapperPath: Map<string, ThemeableComponentRegistryEntry>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.entries = new Set();
|
||||||
|
this.byBaseClass = new Map();
|
||||||
|
this.byBasePath = new Map();
|
||||||
|
this.byWrapperPath = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
public initialize(prefix = '') {
|
||||||
|
if (this.entries.size > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerWrapper(path: string) {
|
||||||
|
const source = getSource(path);
|
||||||
|
|
||||||
|
function traverse(node: any) {
|
||||||
|
if (node.kind === ts.SyntaxKind.Decorator && node.expression.expression.escapedText === 'Component' && node.parent.kind === ts.SyntaxKind.ClassDeclaration) {
|
||||||
|
const wrapperClass = node.parent.name.escapedText;
|
||||||
|
|
||||||
|
for (const heritageClause of node.parent.heritageClauses) {
|
||||||
|
for (const type of heritageClause.types) {
|
||||||
|
if (type.expression.escapedText === 'ThemedComponent') {
|
||||||
|
const baseClass = type.typeArguments[0].typeName?.escapedText;
|
||||||
|
|
||||||
|
ts.forEachChild(source, (topNode: any) => {
|
||||||
|
if (topNode.kind === ts.SyntaxKind.ImportDeclaration) {
|
||||||
|
for (const element of topNode.importClause.namedBindings.elements) {
|
||||||
|
if (element.name.escapedText === baseClass) {
|
||||||
|
const basePath = resolveLocalPath(topNode.moduleSpecifier.text, path);
|
||||||
|
|
||||||
|
themeableComponents.add({
|
||||||
|
baseClass,
|
||||||
|
basePath: basePath.replace(new RegExp(`^${prefix}`), ''),
|
||||||
|
baseFileName: basename(basePath).replace(/\.ts$/, ''),
|
||||||
|
wrapperClass,
|
||||||
|
wrapperPath: path.replace(new RegExp(`^${prefix}`), ''),
|
||||||
|
wrapperFileName: basename(path).replace(/\.ts$/, ''),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
ts.forEachChild(node, traverse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
traverse(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrappers: string[] = glob.GlobSync(prefix + 'src/app/**/themed-*.component.ts', { ignore: 'node_modules/**' }).found;
|
||||||
|
|
||||||
|
for (const wrapper of wrappers) {
|
||||||
|
registerWrapper(wrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private add(entry: ThemeableComponentRegistryEntry) {
|
||||||
|
this.entries.add(entry);
|
||||||
|
this.byBaseClass.set(entry.baseClass, entry);
|
||||||
|
this.byBasePath.set(entry.basePath, entry);
|
||||||
|
this.byWrapperPath.set(entry.wrapperPath, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const themeableComponents = new ThemeableComponentRegistry();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the AST of a TypeScript source file
|
||||||
|
* @param file
|
||||||
|
*/
|
||||||
|
function getSource(file: string): ts.SourceFile {
|
||||||
|
return ts.createSourceFile(
|
||||||
|
file,
|
||||||
|
readFileSync(file).toString(),
|
||||||
|
ts.ScriptTarget.ES2020, // todo: actually use tsconfig.json?
|
||||||
|
/*setParentNodes */ true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a possibly relative local path into an absolute path starting from the root directory of the project
|
||||||
|
*/
|
||||||
|
function resolveLocalPath(path: string, relativeTo: string) {
|
||||||
|
if (path.startsWith('src/')) {
|
||||||
|
return path;
|
||||||
|
} else if (path.startsWith('./')) {
|
||||||
|
const parts = relativeTo.split('/');
|
||||||
|
return [
|
||||||
|
...parts.slice(0, parts.length - 1),
|
||||||
|
path.replace(/^.\//, '')
|
||||||
|
].join('/') + '.ts';
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unsupported local path: ${path}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isThemedComponentWrapper(node: any): boolean {
|
||||||
|
return node.parent.superClass?.name === 'ThemedComponent';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isThemeableComponent(className: string): boolean {
|
||||||
|
themeableComponents.initialize();
|
||||||
|
return themeableComponents.byBaseClass.has(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function inThemedComponentOverrideFile(context: any): boolean {
|
||||||
|
const match = context.getFilename().match(/src\/themes\/[^\/]+\/(app\/.*)/);
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
themeableComponents.initialize();
|
||||||
|
// todo: this is fragile!
|
||||||
|
return themeableComponents.byBasePath.has(`src/${match[1]}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function inThemedComponentFile(context: any): boolean {
|
||||||
|
themeableComponents.initialize();
|
||||||
|
|
||||||
|
return [
|
||||||
|
() => themeableComponents.byBasePath.has(context.getFilename()),
|
||||||
|
() => themeableComponents.byWrapperPath.has(context.getFilename()),
|
||||||
|
() => inThemedComponentOverrideFile(context),
|
||||||
|
].some(predicate => predicate());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function allThemeableComponents(): ThemeableComponentRegistryEntry[] {
|
||||||
|
themeableComponents.initialize();
|
||||||
|
return [...themeableComponents.entries];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getThemeableComponentByBaseClass(baseClass: string): ThemeableComponentRegistryEntry | undefined {
|
||||||
|
themeableComponents.initialize();
|
||||||
|
return themeableComponents.byBaseClass.get(baseClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAllowedUnthemedUsage(usageNode: any) {
|
||||||
|
return isClassDeclaration(usageNode) || isPartOfTypeExpression(usageNode) || isPartOfViewChild(usageNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DISALLOWED_THEME_SELECTORS = 'ds-(base|themed)-';
|
||||||
|
|
||||||
|
export function fixSelectors(text: string): string {
|
||||||
|
return text.replaceAll(/ds-(base|themed)-/g, 'ds-');
|
||||||
|
}
|
9
lint/test/fixture/README.md
Normal file
9
lint/test/fixture/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# ESLint testing fixtures
|
||||||
|
|
||||||
|
The files in this directory are used for the ESLint testing environment
|
||||||
|
- Some rules rely on registries that must be built up _before_ the rule is run
|
||||||
|
- In order to test these registries, the fixture sources contain a few dummy components
|
||||||
|
- The TypeScript ESLint test runner requires at least one dummy file to exist to run any tests
|
||||||
|
- By default, [`test.ts`](./src/test.ts) is used. Note that this file is empty; it's only there for the TypeScript configuration, the actual content is injected from the `code` property in the tests.
|
||||||
|
- To test rules that make assertions based on the path of the file, you'll need to include the `filename` property in the test configuration. Note that it must point to an existing file too!
|
||||||
|
- The `filename` must be provided as `fixture('src/something.ts')`
|
14
lint/test/fixture/src/app/test/test-routing.module.ts
Normal file
14
lint/test/fixture/src/app/test/test-routing.module.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
import { ThemedTestThemeableComponent } from './themed-test-themeable.component';
|
||||||
|
|
||||||
|
export const ROUTES = [
|
||||||
|
{
|
||||||
|
component: ThemedTestThemeableComponent,
|
||||||
|
}
|
||||||
|
];
|
15
lint/test/fixture/src/app/test/test-themeable.component.ts
Normal file
15
lint/test/fixture/src/app/test/test-themeable.component.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-base-test-themeable',
|
||||||
|
template: '',
|
||||||
|
})
|
||||||
|
export class TestThemeableComponent {
|
||||||
|
}
|
8
lint/test/fixture/src/app/test/test.component.spec.ts
Normal file
8
lint/test/fixture/src/app/test/test.component.spec.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
|
15
lint/test/fixture/src/app/test/test.component.ts
Normal file
15
lint/test/fixture/src/app/test/test.component.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-test',
|
||||||
|
template: '',
|
||||||
|
})
|
||||||
|
export class TestComponent {
|
||||||
|
}
|
23
lint/test/fixture/src/app/test/test.module.ts
Normal file
23
lint/test/fixture/src/app/test/test.module.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
// @ts-ignore
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { TestThemeableComponent } from './test-themeable.component';
|
||||||
|
import { TestComponent } from './test.component';
|
||||||
|
import { ThemedTestThemeableComponent } from './themed-test-themeable.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
TestComponent,
|
||||||
|
TestThemeableComponent,
|
||||||
|
ThemedTestThemeableComponent,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class TestModule {
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { ThemedComponent } from '../../../../../../src/app/shared/theme-support/themed.component';
|
||||||
|
import { TestThemeableComponent } from './test-themeable.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-test-themeable',
|
||||||
|
template: '',
|
||||||
|
})
|
||||||
|
export class ThemedTestThemeableComponent extends ThemedComponent<TestThemeableComponent> {
|
||||||
|
protected getComponentName(): string {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected importThemedComponent(themeName: string): Promise<any> {
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected importUnthemedComponent(): Promise<any> {
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
}
|
0
lint/test/fixture/src/test.ts
Normal file
0
lint/test/fixture/src/test.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { TestThemeableComponent as BaseComponent } from '../../../../app/test/test-themeable.component';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-themed-test-themeable',
|
||||||
|
template: '',
|
||||||
|
})
|
||||||
|
export class TestThemeableComponent extends BaseComponent {
|
||||||
|
|
||||||
|
}
|
19
lint/test/fixture/src/themes/test/test.module.ts
Normal file
19
lint/test/fixture/src/themes/test/test.module.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
// @ts-ignore
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { TestThemeableComponent } from './app/test/test-themeable.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
TestThemeableComponent,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class TestModule {
|
||||||
|
|
||||||
|
}
|
7
lint/test/fixture/tsconfig.json
Normal file
7
lint/test/fixture/tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../../tsconfig.json",
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": ["dist"]
|
||||||
|
}
|
13
lint/test/helpers.js
Normal file
13
lint/test/helpers.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
const SpecReporter = require('jasmine-spec-reporter').SpecReporter;
|
||||||
|
const StacktraceOption = require('jasmine-spec-reporter').StacktraceOption;
|
||||||
|
|
||||||
|
jasmine.getEnv().clearReporters(); // Clear default console reporter for those instead
|
||||||
|
jasmine.getEnv().addReporter(new SpecReporter({
|
||||||
|
spec: {
|
||||||
|
displayErrorMessages: false,
|
||||||
|
},
|
||||||
|
summary: {
|
||||||
|
displayFailed: true,
|
||||||
|
displayStacktrace: StacktraceOption.PRETTY,
|
||||||
|
},
|
||||||
|
}));
|
140
lint/test/rules/themed-component-selectors.spec.ts
Normal file
140
lint/test/rules/themed-component-selectors.spec.ts
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import {
|
||||||
|
fixture,
|
||||||
|
tsRuleTester,
|
||||||
|
} from '../testing';
|
||||||
|
import rule from '../../src/rules/ts/themed-component-selectors';
|
||||||
|
|
||||||
|
describe('themed-component-selectors', () => {
|
||||||
|
tsRuleTester.run('themed-component-selectors', rule as any, {
|
||||||
|
valid: [
|
||||||
|
{
|
||||||
|
name: 'Regular non-themeable component selector',
|
||||||
|
code: `
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-something',
|
||||||
|
})
|
||||||
|
class Something {
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Themeable component selector should replace the original version, unthemed version should be changed to ds-base-',
|
||||||
|
code: `
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-base-something',
|
||||||
|
})
|
||||||
|
class Something {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-something',
|
||||||
|
})
|
||||||
|
class ThemedSomething extends ThemedComponent<Something> {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-themed-something',
|
||||||
|
})
|
||||||
|
class OverrideSomething extends Something {
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Other themed component wrappers should not interfere',
|
||||||
|
code: `
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-something',
|
||||||
|
})
|
||||||
|
class Something {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-something-else',
|
||||||
|
})
|
||||||
|
class ThemedSomethingElse extends ThemedComponent<SomethingElse> {
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
invalid: [
|
||||||
|
{
|
||||||
|
name: 'Wrong selector for base component',
|
||||||
|
filename: fixture('src/app/test/test-themeable.component.ts'),
|
||||||
|
code: `
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-something',
|
||||||
|
})
|
||||||
|
class TestThemeableComponent {
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
messageId: 'wrongSelectorUnthemedComponent',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
output: `
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-base-something',
|
||||||
|
})
|
||||||
|
class TestThemeableComponent {
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Wrong selector for wrapper component',
|
||||||
|
filename: fixture('src/app/test/themed-test-themeable.component.ts'),
|
||||||
|
code: `
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-themed-something',
|
||||||
|
})
|
||||||
|
class ThemedTestThemeableComponent extends ThemedComponent<TestThemeableComponent> {
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
messageId: 'wrongSelectorThemedComponentWrapper',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
output: `
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-something',
|
||||||
|
})
|
||||||
|
class ThemedTestThemeableComponent extends ThemedComponent<TestThemeableComponent> {
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Wrong selector for theme override',
|
||||||
|
filename: fixture('src/themes/test/app/test/test-themeable.component.ts'),
|
||||||
|
code: `
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-something',
|
||||||
|
})
|
||||||
|
class TestThememeableComponent extends BaseComponent {
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
messageId: 'wrongSelectorThemedComponentOverride',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
output: `
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-themed-something',
|
||||||
|
})
|
||||||
|
class TestThememeableComponent extends BaseComponent {
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as any);
|
||||||
|
});
|
190
lint/test/rules/themed-component-usages.spec.ts
Normal file
190
lint/test/rules/themed-component-usages.spec.ts
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
fixture,
|
||||||
|
htmlRuleTester,
|
||||||
|
tsRuleTester,
|
||||||
|
} from '../testing';
|
||||||
|
import tsRule from '../../src/rules/ts/themed-component-usages';
|
||||||
|
import htmlRule from '../../src/rules/html/themed-component-usages';
|
||||||
|
|
||||||
|
describe('themed-component-usages (TypeScript)', () => {
|
||||||
|
tsRuleTester.run('themed-component-usages', tsRule as any, {
|
||||||
|
valid: [
|
||||||
|
{
|
||||||
|
code: `
|
||||||
|
const config = {
|
||||||
|
a: ThemedTestThemeableComponent,
|
||||||
|
b: ChipsComponent,
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: `
|
||||||
|
export class TestThemeableComponent {
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: `
|
||||||
|
import { TestThemeableComponent } from '../test/test-themeable.component.ts';
|
||||||
|
|
||||||
|
export class ThemedAdminSidebarComponent extends ThemedComponent<TestThemeableComponent> {
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: `
|
||||||
|
import { TestThemeableComponent } from '../test/test-themeable.component.ts';
|
||||||
|
|
||||||
|
export class Something {
|
||||||
|
@ViewChild(TestThemeableComponent) test: TestThemeableComponent;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: fixture('src/app/test/test.component.spec.ts'),
|
||||||
|
code: `
|
||||||
|
By.css('ds-themeable');
|
||||||
|
By.Css('#test > ds-themeable > #nest');
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
invalid: [
|
||||||
|
{
|
||||||
|
code: `
|
||||||
|
import { TestThemeableComponent } from '../test/test-themeable.component.ts';
|
||||||
|
import { TestComponent } from '../test/test.component.ts';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
a: TestThemeableComponent,
|
||||||
|
b: TestComponent,
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
messageId: 'mustImportThemedWrapper',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
messageId: 'mustUseThemedWrapper',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
output: `
|
||||||
|
import { ThemedTestThemeableComponent } from '../test/themed-test-themeable.component.ts';
|
||||||
|
import { TestComponent } from '../test/test.component.ts';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
a: ThemedTestThemeableComponent,
|
||||||
|
b: TestComponent,
|
||||||
|
}
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: fixture('src/app/test/test.component.spec.ts'),
|
||||||
|
code: `
|
||||||
|
By.css('ds-themed-themeable');
|
||||||
|
By.css('#test > ds-themed-themeable > #nest');
|
||||||
|
`,
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
messageId: 'mustUseThemedWrapper',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
messageId: 'mustUseThemedWrapper',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
output: `
|
||||||
|
By.css('ds-themeable');
|
||||||
|
By.css('#test > ds-themeable > #nest');
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: fixture('src/app/test/test.component.spec.ts'),
|
||||||
|
code: `
|
||||||
|
By.css('ds-base-themeable');
|
||||||
|
By.css('#test > ds-base-themeable > #nest');
|
||||||
|
`,
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
messageId: 'mustUseThemedWrapper',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
messageId: 'mustUseThemedWrapper',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
output: `
|
||||||
|
By.css('ds-themeable');
|
||||||
|
By.css('#test > ds-themeable > #nest');
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as any);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('themed-component-usages (HTML)', () => {
|
||||||
|
htmlRuleTester.run('themed-component-usages', htmlRule, {
|
||||||
|
valid: [
|
||||||
|
{
|
||||||
|
code: `
|
||||||
|
<ds-test-themeable/>
|
||||||
|
<ds-test-themeable></ds-test-themeable>
|
||||||
|
<ds-test-themeable [test]="something"></ds-test-themeable>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
invalid: [
|
||||||
|
{
|
||||||
|
code: `
|
||||||
|
<ds-themed-test-themeable/>
|
||||||
|
<ds-themed-test-themeable></ds-themed-test-themeable>
|
||||||
|
<ds-themed-test-themeable [test]="something"></ds-themed-test-themeable>
|
||||||
|
`,
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
messageId: 'mustUseThemedWrapperSelector',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
messageId: 'mustUseThemedWrapperSelector',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
messageId: 'mustUseThemedWrapperSelector',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
output: `
|
||||||
|
<ds-test-themeable/>
|
||||||
|
<ds-test-themeable></ds-test-themeable>
|
||||||
|
<ds-test-themeable [test]="something"></ds-test-themeable>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: `
|
||||||
|
<ds-base-test-themeable/>
|
||||||
|
<ds-base-test-themeable></ds-base-test-themeable>
|
||||||
|
<ds-base-test-themeable [test]="something"></ds-base-test-themeable>
|
||||||
|
`,
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
messageId: 'mustUseThemedWrapperSelector',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
messageId: 'mustUseThemedWrapperSelector',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
messageId: 'mustUseThemedWrapperSelector',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
output: `
|
||||||
|
<ds-test-themeable/>
|
||||||
|
<ds-test-themeable></ds-test-themeable>
|
||||||
|
<ds-test-themeable [test]="something"></ds-test-themeable>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
});
|
52
lint/test/testing.ts
Normal file
52
lint/test/testing.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { RuleTester } from 'eslint';
|
||||||
|
import { RuleTester as TypeScriptRuleTester } from '@typescript-eslint/rule-tester';
|
||||||
|
import { themeableComponents } from '../src/util/theme-support';
|
||||||
|
|
||||||
|
const FIXTURE = 'lint/test/fixture/';
|
||||||
|
|
||||||
|
// Register themed components from test fixture
|
||||||
|
themeableComponents.initialize(FIXTURE);
|
||||||
|
|
||||||
|
TypeScriptRuleTester.itOnly = fit;
|
||||||
|
|
||||||
|
export function fixture(path: string): string {
|
||||||
|
return FIXTURE + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const tsRuleTester = new TypeScriptRuleTester({
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
defaultFilenames: {
|
||||||
|
ts: fixture('src/test.ts'),
|
||||||
|
tsx: 'n/a',
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
project: fixture('tsconfig.json'),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
class HtmlRuleTester extends RuleTester {
|
||||||
|
run(name: string, rule: any, tests: { valid: any[], invalid: any[] }) {
|
||||||
|
super.run(name, rule, {
|
||||||
|
valid: tests.valid.map((test) => ({
|
||||||
|
filename: fixture('test.html'),
|
||||||
|
...test,
|
||||||
|
})),
|
||||||
|
invalid: tests.invalid.map((test) => ({
|
||||||
|
filename: fixture('test.html'),
|
||||||
|
...test,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const htmlRuleTester = new HtmlRuleTester({
|
||||||
|
parser: require.resolve('@angular-eslint/template-parser'),
|
||||||
|
});
|
24
lint/test/util/theme-support.spec.ts
Normal file
24
lint/test/util/theme-support.spec.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* The contents of this file are subject to the license and copyright
|
||||||
|
* detailed in the LICENSE and NOTICE files at the root of the source
|
||||||
|
* tree and available online at
|
||||||
|
*
|
||||||
|
* http://www.dspace.org/license/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { themeableComponents } from '../../src/util/theme-support';
|
||||||
|
|
||||||
|
describe('theme-support', () => {
|
||||||
|
describe('themeable component registry', () => {
|
||||||
|
it('should contain all themeable components from the fixture', () => {
|
||||||
|
expect(themeableComponents.entries.size).toBe(1);
|
||||||
|
expect(themeableComponents.byBasePath.size).toBe(1);
|
||||||
|
expect(themeableComponents.byWrapperPath.size).toBe(1);
|
||||||
|
expect(themeableComponents.byBaseClass.size).toBe(1);
|
||||||
|
|
||||||
|
expect(themeableComponents.byBaseClass.get('TestThemeableComponent')).toBeTruthy();
|
||||||
|
expect(themeableComponents.byBasePath.get('src/app/test/test-themeable.component.ts')).toBeTruthy();
|
||||||
|
expect(themeableComponents.byWrapperPath.get('src/app/test/themed-test-themeable.component.ts')).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
23
lint/tsconfig.json
Normal file
23
lint/tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "nodenext",
|
||||||
|
"moduleResolution": "nodenext",
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"sourceMap": true,
|
||||||
|
"types": [
|
||||||
|
"jasmine",
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"test/**/*.ts",
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"dist",
|
||||||
|
"test/fixture"
|
||||||
|
]
|
||||||
|
}
|
13
package.json
13
package.json
@@ -17,11 +17,15 @@
|
|||||||
"build:stats": "ng build --stats-json",
|
"build:stats": "ng build --stats-json",
|
||||||
"build:prod": "cross-env NODE_ENV=production yarn run build:ssr",
|
"build:prod": "cross-env NODE_ENV=production yarn run build:ssr",
|
||||||
"build:ssr": "ng build --configuration production && ng run dspace-angular:server:production",
|
"build:ssr": "ng build --configuration production && ng run dspace-angular:server:production",
|
||||||
|
"build:lint": "rimraf 'lint/dist/**/*.js' 'lint/dist/**/*.js.map' && tsc -b lint/tsconfig.json",
|
||||||
"test": "ng test --source-map=true --watch=false --configuration test",
|
"test": "ng test --source-map=true --watch=false --configuration test",
|
||||||
"test:watch": "nodemon --exec \"ng test --source-map=true --watch=true --configuration test\"",
|
"test:watch": "nodemon --exec \"ng test --source-map=true --watch=true --configuration test\"",
|
||||||
"test:headless": "ng test --source-map=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage",
|
"test:headless": "ng test --source-map=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage",
|
||||||
"lint": "ng lint",
|
"test:lint": "yarn build:lint && jasmine --config=lint/jasmine.json",
|
||||||
"lint-fix": "ng lint --fix=true",
|
"test:lint:nobuild": "jasmine --config=lint/jasmine.json",
|
||||||
|
"lint": "yarn build:lint && ng lint",
|
||||||
|
"lint:nobuild": "ng lint",
|
||||||
|
"lint-fix": "yarn build:lint && ng lint --fix=true",
|
||||||
"e2e": "cross-env NODE_ENV=production ng e2e",
|
"e2e": "cross-env NODE_ENV=production ng e2e",
|
||||||
"clean:dev:config": "rimraf src/assets/config.json",
|
"clean:dev:config": "rimraf src/assets/config.json",
|
||||||
"clean:coverage": "rimraf coverage",
|
"clean:coverage": "rimraf coverage",
|
||||||
@@ -94,6 +98,8 @@
|
|||||||
"date-fns-tz": "^1.3.7",
|
"date-fns-tz": "^1.3.7",
|
||||||
"deepmerge": "^4.3.1",
|
"deepmerge": "^4.3.1",
|
||||||
"ejs": "^3.1.9",
|
"ejs": "^3.1.9",
|
||||||
|
"eslint-plugin-dspace-angular-html": "link:./lint/dist/src/rules/html",
|
||||||
|
"eslint-plugin-dspace-angular-ts": "link:./lint/dist/src/rules/ts",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"express-rate-limit": "^5.1.3",
|
"express-rate-limit": "^5.1.3",
|
||||||
"fast-json-patch": "^3.1.1",
|
"fast-json-patch": "^3.1.1",
|
||||||
@@ -160,6 +166,8 @@
|
|||||||
"@types/sanitize-html": "^2.9.0",
|
"@types/sanitize-html": "^2.9.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.1",
|
"@typescript-eslint/eslint-plugin": "^5.59.1",
|
||||||
"@typescript-eslint/parser": "^5.59.1",
|
"@typescript-eslint/parser": "^5.59.1",
|
||||||
|
"@typescript-eslint/rule-tester": "^7.2.0",
|
||||||
|
"@typescript-eslint/utils": "^7.2.0",
|
||||||
"axe-core": "^4.7.2",
|
"axe-core": "^4.7.2",
|
||||||
"compression-webpack-plugin": "^9.2.0",
|
"compression-webpack-plugin": "^9.2.0",
|
||||||
"copy-webpack-plugin": "^6.4.1",
|
"copy-webpack-plugin": "^6.4.1",
|
||||||
@@ -178,6 +186,7 @@
|
|||||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||||
"eslint-plugin-unused-imports": "^2.0.0",
|
"eslint-plugin-unused-imports": "^2.0.0",
|
||||||
"express-static-gzip": "^2.1.7",
|
"express-static-gzip": "^2.1.7",
|
||||||
|
"jasmine": "^3.8.0",
|
||||||
"jasmine-core": "^3.8.0",
|
"jasmine-core": "^3.8.0",
|
||||||
"jasmine-marbles": "0.9.2",
|
"jasmine-marbles": "0.9.2",
|
||||||
"karma": "^6.4.2",
|
"karma": "^6.4.2",
|
||||||
|
@@ -55,6 +55,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"cypress.config.ts"
|
"cypress.config.ts",
|
||||||
|
"lint"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
134
yarn.lock
134
yarn.lock
@@ -1836,7 +1836,7 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.8.tgz#200a0965cf654ac28b971358ecdca9cc5b44c335"
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.8.tgz#200a0965cf654ac28b971358ecdca9cc5b44c335"
|
||||||
integrity sha512-1iuezdyDNngPnz8rLRDO2C/ZZ/emJLb72OsZeqQ6gL6Avko/XCXZw+NuxBSNhBAP13Hie418V7VMt9et1FMvpg==
|
integrity sha512-1iuezdyDNngPnz8rLRDO2C/ZZ/emJLb72OsZeqQ6gL6Avko/XCXZw+NuxBSNhBAP13Hie418V7VMt9et1FMvpg==
|
||||||
|
|
||||||
"@eslint-community/eslint-utils@^4.2.0":
|
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz"
|
resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz"
|
||||||
integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
|
integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
|
||||||
@@ -2565,7 +2565,7 @@
|
|||||||
|
|
||||||
"@types/jasmine@~3.6.0":
|
"@types/jasmine@~3.6.0":
|
||||||
version "3.6.11"
|
version "3.6.11"
|
||||||
resolved "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.11.tgz"
|
resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.6.11.tgz#4b1d77aa9dfc757407cb9e277216d8e83553f09d"
|
||||||
integrity sha512-S6pvzQDvMZHrkBz2Mcn/8Du7cpr76PlRJBAoHnSDNbulULsH5dp0Gns+WRyNX5LHejz/ljxK4/vIHK/caHt6SQ==
|
integrity sha512-S6pvzQDvMZHrkBz2Mcn/8Du7cpr76PlRJBAoHnSDNbulULsH5dp0Gns+WRyNX5LHejz/ljxK4/vIHK/caHt6SQ==
|
||||||
|
|
||||||
"@types/js-cookie@2.2.6":
|
"@types/js-cookie@2.2.6":
|
||||||
@@ -2578,6 +2578,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz"
|
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz"
|
||||||
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
|
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
|
||||||
|
|
||||||
|
"@types/json-schema@^7.0.12":
|
||||||
|
version "7.0.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||||
|
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
|
||||||
|
|
||||||
"@types/json5@^0.0.29":
|
"@types/json5@^0.0.29":
|
||||||
version "0.0.29"
|
version "0.0.29"
|
||||||
resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
|
resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
|
||||||
@@ -2671,6 +2676,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz"
|
resolved "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz"
|
||||||
integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==
|
integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==
|
||||||
|
|
||||||
|
"@types/semver@^7.5.0":
|
||||||
|
version "7.5.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
|
||||||
|
integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
|
||||||
|
|
||||||
"@types/serve-index@^1.9.1":
|
"@types/serve-index@^1.9.1":
|
||||||
version "1.9.1"
|
version "1.9.1"
|
||||||
resolved "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz"
|
resolved "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz"
|
||||||
@@ -2767,6 +2777,17 @@
|
|||||||
"@typescript-eslint/typescript-estree" "5.59.1"
|
"@typescript-eslint/typescript-estree" "5.59.1"
|
||||||
debug "^4.3.4"
|
debug "^4.3.4"
|
||||||
|
|
||||||
|
"@typescript-eslint/rule-tester@^7.2.0":
|
||||||
|
version "7.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/rule-tester/-/rule-tester-7.2.0.tgz#ca72af90fc4d46f1c53a4fc1c28d95fe7a96e879"
|
||||||
|
integrity sha512-V/jxkkx+buBn9uM2QvdHzi1XzxBm2M+QpEORNZCRkq3vKhnZO2Sto1X0xaZ6vVbmHvOE+Zlkv7GO98PXvgGKVg==
|
||||||
|
dependencies:
|
||||||
|
"@typescript-eslint/typescript-estree" "7.2.0"
|
||||||
|
"@typescript-eslint/utils" "7.2.0"
|
||||||
|
ajv "^6.10.0"
|
||||||
|
lodash.merge "4.6.2"
|
||||||
|
semver "^7.5.4"
|
||||||
|
|
||||||
"@typescript-eslint/scope-manager@5.48.2":
|
"@typescript-eslint/scope-manager@5.48.2":
|
||||||
version "5.48.2"
|
version "5.48.2"
|
||||||
resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.2.tgz"
|
resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.2.tgz"
|
||||||
@@ -2799,6 +2820,14 @@
|
|||||||
"@typescript-eslint/types" "5.59.6"
|
"@typescript-eslint/types" "5.59.6"
|
||||||
"@typescript-eslint/visitor-keys" "5.59.6"
|
"@typescript-eslint/visitor-keys" "5.59.6"
|
||||||
|
|
||||||
|
"@typescript-eslint/scope-manager@7.2.0":
|
||||||
|
version "7.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz#cfb437b09a84f95a0930a76b066e89e35d94e3da"
|
||||||
|
integrity sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==
|
||||||
|
dependencies:
|
||||||
|
"@typescript-eslint/types" "7.2.0"
|
||||||
|
"@typescript-eslint/visitor-keys" "7.2.0"
|
||||||
|
|
||||||
"@typescript-eslint/type-utils@5.48.2":
|
"@typescript-eslint/type-utils@5.48.2":
|
||||||
version "5.48.2"
|
version "5.48.2"
|
||||||
resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.2.tgz"
|
resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.2.tgz"
|
||||||
@@ -2839,6 +2868,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.6.tgz#5a6557a772af044afe890d77c6a07e8c23c2460b"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.6.tgz#5a6557a772af044afe890d77c6a07e8c23c2460b"
|
||||||
integrity sha512-tH5lBXZI7T2MOUgOWFdVNUILsI02shyQvfzG9EJkoONWugCG77NDDa1EeDGw7oJ5IvsTAAGVV8I3Tk2PNu9QfA==
|
integrity sha512-tH5lBXZI7T2MOUgOWFdVNUILsI02shyQvfzG9EJkoONWugCG77NDDa1EeDGw7oJ5IvsTAAGVV8I3Tk2PNu9QfA==
|
||||||
|
|
||||||
|
"@typescript-eslint/types@7.2.0":
|
||||||
|
version "7.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.2.0.tgz#0feb685f16de320e8520f13cca30779c8b7c403f"
|
||||||
|
integrity sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@5.48.2":
|
"@typescript-eslint/typescript-estree@5.48.2":
|
||||||
version "5.48.2"
|
version "5.48.2"
|
||||||
resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz"
|
resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz"
|
||||||
@@ -2891,6 +2925,20 @@
|
|||||||
semver "^7.3.7"
|
semver "^7.3.7"
|
||||||
tsutils "^3.21.0"
|
tsutils "^3.21.0"
|
||||||
|
|
||||||
|
"@typescript-eslint/typescript-estree@7.2.0":
|
||||||
|
version "7.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz#5beda2876c4137f8440c5a84b4f0370828682556"
|
||||||
|
integrity sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==
|
||||||
|
dependencies:
|
||||||
|
"@typescript-eslint/types" "7.2.0"
|
||||||
|
"@typescript-eslint/visitor-keys" "7.2.0"
|
||||||
|
debug "^4.3.4"
|
||||||
|
globby "^11.1.0"
|
||||||
|
is-glob "^4.0.3"
|
||||||
|
minimatch "9.0.3"
|
||||||
|
semver "^7.5.4"
|
||||||
|
ts-api-utils "^1.0.1"
|
||||||
|
|
||||||
"@typescript-eslint/utils@5.48.2":
|
"@typescript-eslint/utils@5.48.2":
|
||||||
version "5.48.2"
|
version "5.48.2"
|
||||||
resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.2.tgz"
|
resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.2.tgz"
|
||||||
@@ -2933,6 +2981,19 @@
|
|||||||
eslint-scope "^5.1.1"
|
eslint-scope "^5.1.1"
|
||||||
semver "^7.3.7"
|
semver "^7.3.7"
|
||||||
|
|
||||||
|
"@typescript-eslint/utils@7.2.0", "@typescript-eslint/utils@^7.2.0":
|
||||||
|
version "7.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.2.0.tgz#fc8164be2f2a7068debb4556881acddbf0b7ce2a"
|
||||||
|
integrity sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==
|
||||||
|
dependencies:
|
||||||
|
"@eslint-community/eslint-utils" "^4.4.0"
|
||||||
|
"@types/json-schema" "^7.0.12"
|
||||||
|
"@types/semver" "^7.5.0"
|
||||||
|
"@typescript-eslint/scope-manager" "7.2.0"
|
||||||
|
"@typescript-eslint/types" "7.2.0"
|
||||||
|
"@typescript-eslint/typescript-estree" "7.2.0"
|
||||||
|
semver "^7.5.4"
|
||||||
|
|
||||||
"@typescript-eslint/utils@^5.57.0":
|
"@typescript-eslint/utils@^5.57.0":
|
||||||
version "5.58.0"
|
version "5.58.0"
|
||||||
resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.58.0.tgz"
|
resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.58.0.tgz"
|
||||||
@@ -2979,6 +3040,14 @@
|
|||||||
"@typescript-eslint/types" "5.59.6"
|
"@typescript-eslint/types" "5.59.6"
|
||||||
eslint-visitor-keys "^3.3.0"
|
eslint-visitor-keys "^3.3.0"
|
||||||
|
|
||||||
|
"@typescript-eslint/visitor-keys@7.2.0":
|
||||||
|
version "7.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz#5035f177752538a5750cca1af6044b633610bf9e"
|
||||||
|
integrity sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==
|
||||||
|
dependencies:
|
||||||
|
"@typescript-eslint/types" "7.2.0"
|
||||||
|
eslint-visitor-keys "^3.4.1"
|
||||||
|
|
||||||
"@webassemblyjs/ast@1.11.1":
|
"@webassemblyjs/ast@1.11.1":
|
||||||
version "1.11.1"
|
version "1.11.1"
|
||||||
resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz"
|
resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz"
|
||||||
@@ -5454,6 +5523,14 @@ eslint-plugin-deprecation@^1.4.1:
|
|||||||
tslib "^2.3.1"
|
tslib "^2.3.1"
|
||||||
tsutils "^3.21.0"
|
tsutils "^3.21.0"
|
||||||
|
|
||||||
|
"eslint-plugin-dspace-angular-html@link:./lint/dist/src/rules/html":
|
||||||
|
version "0.0.0"
|
||||||
|
uid ""
|
||||||
|
|
||||||
|
"eslint-plugin-dspace-angular-ts@link:./lint/dist/src/rules/ts":
|
||||||
|
version "0.0.0"
|
||||||
|
uid ""
|
||||||
|
|
||||||
eslint-plugin-import-newlines@^1.3.1:
|
eslint-plugin-import-newlines@^1.3.1:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-plugin-import-newlines/-/eslint-plugin-import-newlines-1.3.1.tgz#e21705667778e8134382b50079fbb2c8d3a2fcde"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-import-newlines/-/eslint-plugin-import-newlines-1.3.1.tgz#e21705667778e8134382b50079fbb2c8d3a2fcde"
|
||||||
@@ -5583,6 +5660,11 @@ eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4
|
|||||||
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz"
|
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz"
|
||||||
integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==
|
integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==
|
||||||
|
|
||||||
|
eslint-visitor-keys@^3.4.1:
|
||||||
|
version "3.4.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
|
||||||
|
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
|
||||||
|
|
||||||
eslint@^8.39.0:
|
eslint@^8.39.0:
|
||||||
version "8.39.0"
|
version "8.39.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.39.0.tgz#7fd20a295ef92d43809e914b70c39fd5a23cf3f1"
|
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.39.0.tgz#7fd20a295ef92d43809e914b70c39fd5a23cf3f1"
|
||||||
@@ -7296,7 +7378,7 @@ jake@^10.8.5:
|
|||||||
filelist "^1.0.1"
|
filelist "^1.0.1"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
|
|
||||||
jasmine-core@^3.6.0, jasmine-core@^3.8.0:
|
jasmine-core@^3.6.0, jasmine-core@^3.8.0, jasmine-core@~3.99.0:
|
||||||
version "3.99.1"
|
version "3.99.1"
|
||||||
resolved "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.99.1.tgz"
|
resolved "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.99.1.tgz"
|
||||||
integrity sha512-Hu1dmuoGcZ7AfyynN3LsfruwMbxMALMka+YtZeGoLuDEySVmVAPaonkNoBRIw/ectu8b9tVQCJNgp4a4knp+tg==
|
integrity sha512-Hu1dmuoGcZ7AfyynN3LsfruwMbxMALMka+YtZeGoLuDEySVmVAPaonkNoBRIw/ectu8b9tVQCJNgp4a4knp+tg==
|
||||||
@@ -7308,6 +7390,14 @@ jasmine-marbles@0.9.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
lodash "^4.17.20"
|
lodash "^4.17.20"
|
||||||
|
|
||||||
|
jasmine@^3.8.0:
|
||||||
|
version "3.99.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.99.0.tgz#7cc7aeda7ade2d57694fc818a374f778cbb4ea62"
|
||||||
|
integrity sha512-YIThBuHzaIIcjxeuLmPD40SjxkEcc8i//sGMDKCgkRMVgIwRJf5qyExtlJpQeh7pkeoBSOe6lQEdg+/9uKg9mw==
|
||||||
|
dependencies:
|
||||||
|
glob "^7.1.6"
|
||||||
|
jasmine-core "~3.99.0"
|
||||||
|
|
||||||
jest-worker@^27.4.5:
|
jest-worker@^27.4.5:
|
||||||
version "27.5.1"
|
version "27.5.1"
|
||||||
resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz"
|
resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz"
|
||||||
@@ -7885,7 +7975,7 @@ lodash.isfinite@^3.3.2:
|
|||||||
resolved "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz"
|
resolved "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz"
|
||||||
integrity sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==
|
integrity sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==
|
||||||
|
|
||||||
lodash.merge@^4.6.2:
|
lodash.merge@4.6.2, lodash.merge@^4.6.2:
|
||||||
version "4.6.2"
|
version "4.6.2"
|
||||||
resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
|
resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
|
||||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||||
@@ -8231,6 +8321,13 @@ minimalistic-assert@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz"
|
resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz"
|
||||||
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
|
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
|
||||||
|
|
||||||
|
minimatch@9.0.3, minimatch@^9.0.0:
|
||||||
|
version "9.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
|
||||||
|
integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
|
||||||
|
dependencies:
|
||||||
|
brace-expansion "^2.0.1"
|
||||||
|
|
||||||
minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
|
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
|
||||||
@@ -8259,13 +8356,6 @@ minimatch@^8.0.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^2.0.1"
|
brace-expansion "^2.0.1"
|
||||||
|
|
||||||
minimatch@^9.0.0:
|
|
||||||
version "9.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
|
|
||||||
integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
|
|
||||||
dependencies:
|
|
||||||
brace-expansion "^2.0.1"
|
|
||||||
|
|
||||||
minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.8:
|
minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.8:
|
||||||
version "1.2.8"
|
version "1.2.8"
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||||
@@ -10537,7 +10627,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||||
|
|
||||||
semver@^7.5.1:
|
semver@^7.5.1, semver@^7.5.4:
|
||||||
version "7.6.0"
|
version "7.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
|
||||||
integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
|
integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
|
||||||
@@ -11362,6 +11452,11 @@ tree-kill@1.2.2, tree-kill@^1.2.2:
|
|||||||
resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz"
|
resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz"
|
||||||
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
|
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
|
||||||
|
|
||||||
|
ts-api-utils@^1.0.1:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.2.1.tgz#f716c7e027494629485b21c0df6180f4d08f5e8b"
|
||||||
|
integrity sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==
|
||||||
|
|
||||||
ts-node@10.2.1, ts-node@^10.0.0:
|
ts-node@10.2.1, ts-node@^10.0.0:
|
||||||
version "10.2.1"
|
version "10.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.2.1.tgz#4cc93bea0a7aba2179497e65bb08ddfc198b3ab5"
|
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.2.1.tgz#4cc93bea0a7aba2179497e65bb08ddfc198b3ab5"
|
||||||
@@ -12303,7 +12398,7 @@ yargs@17.1.1:
|
|||||||
y18n "^5.0.5"
|
y18n "^5.0.5"
|
||||||
yargs-parser "^20.2.2"
|
yargs-parser "^20.2.2"
|
||||||
|
|
||||||
yargs@17.7.2:
|
yargs@17.7.2, yargs@^17.0.0:
|
||||||
version "17.7.2"
|
version "17.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
|
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
|
||||||
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
|
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
|
||||||
@@ -12329,19 +12424,6 @@ yargs@^16.1.1:
|
|||||||
y18n "^5.0.5"
|
y18n "^5.0.5"
|
||||||
yargs-parser "^20.2.2"
|
yargs-parser "^20.2.2"
|
||||||
|
|
||||||
yargs@^17.0.0:
|
|
||||||
version "17.7.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
|
|
||||||
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
|
|
||||||
dependencies:
|
|
||||||
cliui "^8.0.1"
|
|
||||||
escalade "^3.1.1"
|
|
||||||
get-caller-file "^2.0.5"
|
|
||||||
require-directory "^2.1.1"
|
|
||||||
string-width "^4.2.3"
|
|
||||||
y18n "^5.0.5"
|
|
||||||
yargs-parser "^21.1.1"
|
|
||||||
|
|
||||||
yargs@^17.2.1, yargs@^17.3.1:
|
yargs@^17.2.1, yargs@^17.3.1:
|
||||||
version "17.7.1"
|
version "17.7.1"
|
||||||
resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz"
|
resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz"
|
||||||
|
Reference in New Issue
Block a user