@rushstack/eslint-plugin-packlets
Advanced tools
Comparing version 0.1.1 to 0.1.2
@@ -5,2 +5,20 @@ { | ||
{ | ||
"version": "0.1.2", | ||
"tag": "@rushstack/eslint-plugin-packlets_v0.1.2", | ||
"date": "Wed, 28 Oct 2020 01:18:03 GMT", | ||
"comments": { | ||
"patch": [ | ||
{ | ||
"comment": "Fix an exception that occured if a source file was added to the \"src/packlets\" folder, not belonging to any packlet" | ||
}, | ||
{ | ||
"comment": "Fix an issue where linting was sometimes not performed on MacOS, because Node.js \"path.relative()\" incorrectly assumes that every POSIX file system is case-sensitive" | ||
}, | ||
{ | ||
"comment": "Fix an issue where @rushstack/packlets/circular-deps did not detect certain types of circular dependencies" | ||
} | ||
] | ||
} | ||
}, | ||
{ | ||
"version": "0.1.1", | ||
@@ -7,0 +25,0 @@ "tag": "@rushstack/eslint-plugin-packlets_v0.1.1", |
# Change Log - @rushstack/eslint-plugin-packlets | ||
This log was last generated on Tue, 06 Oct 2020 00:24:06 GMT and should not be manually modified. | ||
This log was last generated on Wed, 28 Oct 2020 01:18:03 GMT and should not be manually modified. | ||
## 0.1.2 | ||
Wed, 28 Oct 2020 01:18:03 GMT | ||
### Patches | ||
- Fix an exception that occured if a source file was added to the "src/packlets" folder, not belonging to any packlet | ||
- Fix an issue where linting was sometimes not performed on MacOS, because Node.js "path.relative()" incorrectly assumes that every POSIX file system is case-sensitive | ||
- Fix an issue where @rushstack/packlets/circular-deps did not detect certain types of circular dependencies | ||
## 0.1.1 | ||
@@ -6,0 +15,0 @@ Tue, 06 Oct 2020 00:24:06 GMT |
"use strict"; | ||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. | ||
// See LICENSE in the project root for license information. | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.circularDeps = void 0; | ||
const path = __importStar(require("path")); | ||
const experimental_utils_1 = require("@typescript-eslint/experimental-utils"); | ||
const PackletAnalyzer_1 = require("./PackletAnalyzer"); | ||
const DependencyAnalyzer_1 = require("./DependencyAnalyzer"); | ||
const Path_1 = require("./Path"); | ||
const circularDeps = { | ||
@@ -64,3 +45,3 @@ meta: { | ||
if (packletImports) { | ||
const tsconfigFileFolder = path.dirname(tsconfigFilePath); | ||
const tsconfigFileFolder = Path_1.Path.dirname(tsconfigFilePath); | ||
const affectedPackletNames = packletImports.map((x) => x.packletName); | ||
@@ -73,3 +54,3 @@ // If 3 different packlets form a circular dependency, we don't need to report the same warning 3 times. | ||
for (const packletImport of packletImports) { | ||
const filePath = path.relative(tsconfigFileFolder, packletImport.fromFilePath); | ||
const filePath = Path_1.Path.relative(tsconfigFileFolder, packletImport.fromFilePath); | ||
report += `"${packletImport.packletName}" is referenced by ${filePath}\n`; | ||
@@ -76,0 +57,0 @@ } |
"use strict"; | ||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. | ||
// See LICENSE in the project root for license information. | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.DependencyAnalyzer = void 0; | ||
const path = __importStar(require("path")); | ||
const Path_1 = require("./Path"); | ||
@@ -45,3 +25,4 @@ var RefFileKind; | ||
static _walkImports(packletName, startingPackletName, refFileMap, program, packletsFolderPath, visitedPacklets, previousNode) { | ||
const packletEntryPoint = path.join(packletsFolderPath, packletName, 'index'); | ||
visitedPacklets.add(packletName); | ||
const packletEntryPoint = Path_1.Path.join(packletsFolderPath, packletName, 'index'); | ||
const tsSourceFile = program.getSourceFile(packletEntryPoint + '.ts') || program.getSourceFile(packletEntryPoint + '.tsx'); | ||
@@ -60,9 +41,24 @@ if (!tsSourceFile) { | ||
if (Path_1.Path.isUnder(referencingFilePath, packletsFolderPath)) { | ||
const referencingRelativePath = path.relative(packletsFolderPath, referencingFilePath); | ||
const referencingRelativePath = Path_1.Path.relative(packletsFolderPath, referencingFilePath); | ||
const referencingPathParts = referencingRelativePath.split(/[\/\\]+/); | ||
const referencingPackletName = referencingPathParts[0]; | ||
// Did we return to where we started from? | ||
if (referencingPackletName === startingPackletName) { | ||
// Ignore the degenerate case where the starting node imports itself, | ||
// since @rushstack/packlets/mechanics will already report that. | ||
if (previousNode) { | ||
// Make a new linked list node to record this step of the traversal | ||
const importListNode = { | ||
previousNode: previousNode, | ||
fromFilePath: referencingFilePath, | ||
packletName: packletName | ||
}; | ||
// The traversal has returned to the packlet that we started from; | ||
// this means we have detected a circular dependency | ||
return importListNode; | ||
} | ||
} | ||
// Have we already analyzed this packlet? | ||
if (!visitedPacklets.has(packletName)) { | ||
visitedPacklets.add(packletName); | ||
// Make a new linked list node to record this step of the traversal | ||
if (!visitedPacklets.has(referencingPackletName)) { | ||
// Make a new linked list node to record this step of the traversal | ||
const importListNode = { | ||
@@ -73,7 +69,2 @@ previousNode: previousNode, | ||
}; | ||
if (referencingPackletName === startingPackletName) { | ||
// The traversal has returned to the packlet that we started from; | ||
// this means we have detected a circular dependency | ||
return importListNode; | ||
} | ||
const result = DependencyAnalyzer._walkImports(referencingPackletName, startingPackletName, refFileMap, program, packletsFolderPath, visitedPacklets, importListNode); | ||
@@ -80,0 +71,0 @@ if (result) { |
@@ -12,9 +12,12 @@ "use strict"; | ||
messages: { | ||
// InputFileMessageIds | ||
'file-in-packets-folder': 'The "packlets" folder must not contain regular source files', | ||
'invalid-packlet-name': 'Invalid packlet name "{{packletName}}".' + | ||
' The name must be lowercase alphanumeric words separated by hyphens. Example: "my-packlet"', | ||
'misplaced-packlets-folder': 'The packlets folder must be located at "{{expectedPackletsFolder}}"', | ||
'missing-src-folder': 'Expecting to find a "src" folder at: {{srcFolderPath}}', | ||
'missing-tsconfig': 'In order to use @rushstack/eslint-plugin-packlets, your ESLint config file' + | ||
' must configure the TypeScript parser', | ||
'missing-src-folder': 'Expecting to find a "src" folder at: {{srcFolderPath}}', | ||
'packlet-folder-case': 'The packlets folder must be all lower case: {{packletsFolderPath}}', | ||
'invalid-packlet-name': 'Invalid packlet name "{{packletName}}".' + | ||
' The name must be lowercase alphanumeric words separated by hyphens. Example: "my-packlet"', | ||
'misplaced-packlets-folder': 'The packlets folder must be located at "{{expectedPackletsFolder}}"', | ||
// ImportMessageIds | ||
'bypassed-entry-point': 'The import statement does not use the packlet\'s entry point "{{entryPointModulePath}}"', | ||
@@ -21,0 +24,0 @@ 'circular-entry-point': 'Files under a packlet folder must not import from their own index.ts file', |
@@ -1,2 +0,2 @@ | ||
export declare type InputFileMessageIds = 'missing-tsconfig' | 'missing-src-folder' | 'packlet-folder-case' | 'invalid-packlet-name' | 'misplaced-packlets-folder'; | ||
export declare type InputFileMessageIds = 'file-in-packets-folder' | 'invalid-packlet-name' | 'misplaced-packlets-folder' | 'missing-src-folder' | 'missing-tsconfig' | 'packlet-folder-case'; | ||
export declare type ImportMessageIds = 'bypassed-entry-point' | 'circular-entry-point' | 'packlet-importing-project-file'; | ||
@@ -3,0 +3,0 @@ export interface IAnalyzerError { |
@@ -25,3 +25,2 @@ "use strict"; | ||
exports.PackletAnalyzer = void 0; | ||
const path = __importStar(require("path")); | ||
const fs = __importStar(require("fs")); | ||
@@ -44,3 +43,3 @@ const Path_1 = require("./Path"); | ||
} | ||
srcFolderPath = path.join(path.dirname(tsconfigFilePath), 'src'); | ||
srcFolderPath = Path_1.Path.join(Path_1.Path.dirname(tsconfigFilePath), 'src'); | ||
if (!fs.existsSync(srcFolderPath)) { | ||
@@ -56,7 +55,7 @@ this.error = { messageId: 'missing-src-folder', data: { srcFolderPath } }; | ||
// Example: packlets/my-packlet/index.ts | ||
const inputFilePathRelativeToSrc = path.relative(srcFolderPath, inputFilePath); | ||
const inputFilePathRelativeToSrc = Path_1.Path.relative(srcFolderPath, inputFilePath); | ||
// Example: [ 'packlets', 'my-packlet', 'index.ts' ] | ||
const pathParts = inputFilePathRelativeToSrc.split(/[\/\\]+/); | ||
let underPackletsFolder = false; | ||
const expectedPackletsFolder = path.join(srcFolderPath, 'packlets'); | ||
const expectedPackletsFolder = Path_1.Path.join(srcFolderPath, 'packlets'); | ||
for (let i = 0; i < pathParts.length; ++i) { | ||
@@ -67,3 +66,3 @@ const pathPart = pathParts[i]; | ||
// Example: /path/to/my-project/src/PACKLETS | ||
const packletsFolderPath = path.join(srcFolderPath, ...pathParts.slice(0, i + 1)); | ||
const packletsFolderPath = Path_1.Path.join(srcFolderPath, ...pathParts.slice(0, i + 1)); | ||
this.error = { messageId: 'packlet-folder-case', data: { packletsFolderPath } }; | ||
@@ -84,16 +83,25 @@ return; | ||
} | ||
if (underPackletsFolder && pathParts.length >= 2) { | ||
// Example: 'my-packlet' | ||
const packletName = pathParts[1]; | ||
this.inputFilePackletName = packletName; | ||
// Example: 'index.ts' or 'index.tsx' | ||
const thirdPart = pathParts[2]; | ||
// Example: 'index' | ||
const thirdPartWithoutExtension = path.parse(thirdPart).name; | ||
if (thirdPartWithoutExtension.toUpperCase() === 'INDEX') { | ||
if (!PackletAnalyzer._validPackletName.test(packletName)) { | ||
this.error = { messageId: 'invalid-packlet-name', data: { packletName } }; | ||
return; | ||
if (underPackletsFolder) { | ||
if (pathParts.length === 2) { | ||
// Example: src/packlets/SomeFile.ts | ||
this.error = { messageId: 'file-in-packets-folder' }; | ||
return; | ||
} | ||
if (pathParts.length >= 2) { | ||
// Example: 'my-packlet' | ||
const packletName = pathParts[1]; | ||
this.inputFilePackletName = packletName; | ||
if (pathParts.length === 3) { | ||
// Example: 'index.ts' or 'index.tsx' | ||
const thirdPart = pathParts[2]; | ||
// Example: 'index' | ||
const thirdPartWithoutExtension = Path_1.Path.parse(thirdPart).name; | ||
if (thirdPartWithoutExtension.toUpperCase() === 'INDEX') { | ||
if (!PackletAnalyzer._validPackletName.test(packletName)) { | ||
this.error = { messageId: 'invalid-packlet-name', data: { packletName } }; | ||
return; | ||
} | ||
this.isEntryPoint = true; | ||
} | ||
} | ||
this.isEntryPoint = true; | ||
} | ||
@@ -114,9 +122,9 @@ } | ||
// Example: /path/to/my-project/src/packlets/my-packlet | ||
const inputFileFolder = path.dirname(this.inputFilePath); | ||
const inputFileFolder = Path_1.Path.dirname(this.inputFilePath); | ||
// Example: /path/to/my-project/src/other-packlet/index | ||
const importedPath = path.resolve(inputFileFolder, modulePath); | ||
const importedPath = Path_1.Path.resolve(inputFileFolder, modulePath); | ||
// Is the imported path referring to a file under the src/packlets folder? | ||
if (Path_1.Path.isUnder(importedPath, this.packletsFolderPath)) { | ||
// Example: other-packlet/index | ||
const importedPathRelativeToPackletsFolder = path.relative(this.packletsFolderPath, importedPath); | ||
const importedPathRelativeToPackletsFolder = Path_1.Path.relative(this.packletsFolderPath, importedPath); | ||
// Example: [ 'other-packlet', 'index' ] | ||
@@ -134,3 +142,3 @@ const importedPathParts = importedPathRelativeToPackletsFolder.split(/[\/\\]+/); | ||
// import { X } from "../index.js"; | ||
const lastPart = path.parse(importedPathParts[importedPathParts.length - 1]).name; | ||
const lastPart = Path_1.Path.parse(importedPathParts[importedPathParts.length - 1]).name; | ||
let pathToCompare; | ||
@@ -141,3 +149,3 @@ if (lastPart.toUpperCase() === 'INDEX') { | ||
// pathToCompare = /path/to/my-project/src/other-packlet | ||
pathToCompare = path.dirname(importedPath); | ||
pathToCompare = Path_1.Path.dirname(importedPath); | ||
} | ||
@@ -148,3 +156,3 @@ else { | ||
// Example: /path/to/my-project/src/other-packlet | ||
const entryPointPath = path.join(this.packletsFolderPath, importedPackletName); | ||
const entryPointPath = Path_1.Path.join(this.packletsFolderPath, importedPackletName); | ||
if (Path_1.Path.isEqual(pathToCompare, entryPointPath)) { | ||
@@ -160,6 +168,6 @@ return { | ||
// Example: /path/to/my-project/src/other-packlet | ||
const entryPointPath = path.join(this.packletsFolderPath, importedPackletName); | ||
const entryPointPath = Path_1.Path.join(this.packletsFolderPath, importedPackletName); | ||
if (!Path_1.Path.isEqual(importedPath, entryPointPath)) { | ||
// Example: "../packlets/other-packlet" | ||
const entryPointModulePath = Path_1.Path.convertToSlashes(path.relative(inputFileFolder, entryPointPath)); | ||
const entryPointModulePath = Path_1.Path.convertToSlashes(Path_1.Path.relative(inputFileFolder, entryPointPath)); | ||
return { | ||
@@ -166,0 +174,0 @@ messageId: 'bypassed-entry-point', |
@@ -0,2 +1,35 @@ | ||
/// <reference types="node" /> | ||
import * as path from 'path'; | ||
export declare type ParsedPath = path.ParsedPath; | ||
export declare class Path { | ||
/** | ||
* Whether the filesystem is assumed to be case sensitive for Path operations. | ||
* | ||
* @remarks | ||
* Regardless of operating system, a given file system's paths may be case-sensitive or case-insensitive. | ||
* If a volume is mounted under a subfolder, then different parts of a path can even have different | ||
* case-sensitivity. The Node.js "path" API naively assumes that all Windows paths are case-insensitive, | ||
* and that all other OS's are case-sensitive. This is way off, for example a modern MacBook has a | ||
* case-insensitive filesystem by default. There isn't an easy workaround because Node.js does not expose | ||
* the native OS APIs that would give accurate answers. | ||
* | ||
* The TypeScript compiler does somewhat better: it performs an empirical test of its own bundle path to see | ||
* whether it can be read using different case. If so, it normalizes all paths to lowercase (sometimes with | ||
* no API for retrieving the real path). This caused our Path.isUnder() to return incorrect answers because | ||
* it relies on Node.js path.relative(). | ||
* | ||
* To solve that problem, Path.ts performs an empirical test similar to what the TypeScript compiler does, | ||
* and then we adjust path.relative() to be case insensitive if appropriate. | ||
* | ||
* @see {@link https://nodejs.org/en/docs/guides/working-with-different-filesystems/} | ||
*/ | ||
static usingCaseSensitive: boolean; | ||
private static _detectCaseSensitive; | ||
private static _trimTrailingSlashes; | ||
private static _relativeCaseInsensitive; | ||
static relative(from: string, to: string): string; | ||
static dirname(p: string): string; | ||
static join(...paths: string[]): string; | ||
static resolve(...pathSegments: string[]): string; | ||
static parse(pathString: string): ParsedPath; | ||
private static _relativePathRegex; | ||
@@ -3,0 +36,0 @@ /** |
107
lib/Path.js
@@ -26,4 +26,81 @@ "use strict"; | ||
const path = __importStar(require("path")); | ||
// These helpers are borrowed from @rushstack/node-core-library | ||
const fs = __importStar(require("fs")); | ||
class Path { | ||
static _detectCaseSensitive() { | ||
// Can our own file be accessed using a path with different case? If so, then the filesystem is case-insensitive. | ||
return !fs.existsSync(__filename.toUpperCase()); | ||
} | ||
// Removes redundant trailing slashes from a path. | ||
static _trimTrailingSlashes(inputPath) { | ||
// Examples: | ||
// "/a/b///\\" --> "/a/b" | ||
// "/" --> "/" | ||
return inputPath.replace(/(?<=[^\/\\])[\/\\]+$/, ''); | ||
} | ||
// An implementation of path.relative() that is case-insensitive. | ||
static _relativeCaseInsensitive(from, to) { | ||
// path.relative() apples path.normalize() and also trims any trailing slashes. | ||
// Since we'll be matching toNormalized against result, we need to do that for our string as well. | ||
const normalizedTo = Path._trimTrailingSlashes(path.normalize(to)); | ||
// We start by converting everything to uppercase and call path.relative() | ||
const uppercasedFrom = from.toUpperCase(); | ||
const uppercasedTo = normalizedTo.toUpperCase(); | ||
// The result will be all uppercase because its inputs were uppercased | ||
const uppercasedResult = path.relative(uppercasedFrom, uppercasedTo); | ||
// Are there any cased characters in the result? | ||
if (uppercasedResult.toLowerCase() === uppercasedResult) { | ||
// No cased characters | ||
// Example: "../.." | ||
return uppercasedResult; | ||
} | ||
// Example: | ||
// from="/a/b/c" | ||
// to="/a/b/d/e" | ||
// | ||
// fromNormalized="/A/B/C" | ||
// toNormalized="/A/B/D/E" | ||
// | ||
// result="../D/E" | ||
// | ||
// Scan backwards comparing uppercasedResult versus uppercasedTo, stopping at the first place where they differ. | ||
let resultIndex = uppercasedResult.length; | ||
let toIndex = normalizedTo.length; | ||
for (;;) { | ||
if (resultIndex === 0 || toIndex === 0) { | ||
// Stop if we reach the start of the string | ||
break; | ||
} | ||
if (uppercasedResult.charCodeAt(resultIndex - 1) !== uppercasedTo.charCodeAt(toIndex - 1)) { | ||
// Stop before we reach a character that is different | ||
break; | ||
} | ||
--resultIndex; | ||
--toIndex; | ||
} | ||
// Replace the matching part with the properly cased substring from the "normalizedTo" input | ||
// | ||
// Example: | ||
// ".." + "/d/e" = "../d/e" | ||
return uppercasedResult.substring(0, resultIndex) + normalizedTo.substring(toIndex); | ||
} | ||
static relative(from, to) { | ||
if (!Path.usingCaseSensitive) { | ||
return Path._relativeCaseInsensitive(from, to); | ||
} | ||
return path.relative(from, to); | ||
} | ||
// -------------------------------------------------------------------------------------------------------- | ||
// The operations below don't care about case sensitivity | ||
static dirname(p) { | ||
return path.dirname(p); | ||
} | ||
static join(...paths) { | ||
return path.join(...paths); | ||
} | ||
static resolve(...pathSegments) { | ||
return path.resolve(...pathSegments); | ||
} | ||
static parse(pathString) { | ||
return path.parse(pathString); | ||
} | ||
/** | ||
@@ -40,3 +117,3 @@ * Returns true if "childPath" is located inside the "parentFolderPath" folder | ||
static isUnder(childPath, parentFolderPath) { | ||
const relativePath = path.relative(childPath, parentFolderPath); | ||
const relativePath = Path.relative(childPath, parentFolderPath); | ||
return Path._relativePathRegex.test(relativePath); | ||
@@ -52,3 +129,3 @@ } | ||
static isEqual(path1, path2) { | ||
return path.relative(path1, path2) === ''; | ||
return Path.relative(path1, path2) === ''; | ||
} | ||
@@ -66,3 +143,27 @@ /** | ||
exports.Path = Path; | ||
/** | ||
* Whether the filesystem is assumed to be case sensitive for Path operations. | ||
* | ||
* @remarks | ||
* Regardless of operating system, a given file system's paths may be case-sensitive or case-insensitive. | ||
* If a volume is mounted under a subfolder, then different parts of a path can even have different | ||
* case-sensitivity. The Node.js "path" API naively assumes that all Windows paths are case-insensitive, | ||
* and that all other OS's are case-sensitive. This is way off, for example a modern MacBook has a | ||
* case-insensitive filesystem by default. There isn't an easy workaround because Node.js does not expose | ||
* the native OS APIs that would give accurate answers. | ||
* | ||
* The TypeScript compiler does somewhat better: it performs an empirical test of its own bundle path to see | ||
* whether it can be read using different case. If so, it normalizes all paths to lowercase (sometimes with | ||
* no API for retrieving the real path). This caused our Path.isUnder() to return incorrect answers because | ||
* it relies on Node.js path.relative(). | ||
* | ||
* To solve that problem, Path.ts performs an empirical test similar to what the TypeScript compiler does, | ||
* and then we adjust path.relative() to be case insensitive if appropriate. | ||
* | ||
* @see {@link https://nodejs.org/en/docs/guides/working-with-different-filesystems/} | ||
*/ | ||
Path.usingCaseSensitive = Path._detectCaseSensitive(); | ||
// -------------------------------------------------------------------------------------------------------- | ||
// The operations below are borrowed from @rushstack/node-core-library | ||
Path._relativePathRegex = /^[.\/\\]+$/; | ||
//# sourceMappingURL=Path.js.map |
{ | ||
"name": "@rushstack/eslint-plugin-packlets", | ||
"version": "0.1.1", | ||
"version": "0.1.2", | ||
"description": "A lightweight alternative to NPM packages for organizing source files within a single project", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
105524
892
3