@atlaskit/eslint-plugin-platform 2.7.2 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/cjs/index.js +14 -3
- package/dist/cjs/rules/feature-gating/valid-gate-name/index.js +60 -0
- package/dist/cjs/rules/import/no-barrel-entry-imports/index.js +871 -0
- package/dist/cjs/rules/import/no-barrel-entry-jest-mock/index.js +1384 -0
- package/dist/cjs/rules/import/no-conversation-assistant-barrel-imports/index.js +43 -0
- package/dist/cjs/rules/import/no-jest-mock-barrel-files/index.js +1401 -0
- package/dist/cjs/rules/import/no-relative-barrel-file-imports/index.js +777 -0
- package/dist/cjs/rules/import/shared/barrel-parsing.js +511 -0
- package/dist/cjs/rules/import/shared/file-system.js +186 -0
- package/dist/cjs/rules/import/shared/jest-utils.js +191 -0
- package/dist/cjs/rules/import/shared/package-registry.js +263 -0
- package/dist/cjs/rules/import/shared/package-resolution.js +185 -0
- package/dist/cjs/rules/import/shared/perf.js +89 -0
- package/dist/cjs/rules/import/shared/types.js +67 -0
- package/dist/es2019/index.js +14 -3
- package/dist/es2019/rules/feature-gating/valid-gate-name/index.js +52 -0
- package/dist/es2019/rules/import/no-barrel-entry-imports/index.js +801 -0
- package/dist/es2019/rules/import/no-barrel-entry-jest-mock/index.js +1113 -0
- package/dist/es2019/rules/import/no-conversation-assistant-barrel-imports/index.js +37 -0
- package/dist/es2019/rules/import/no-jest-mock-barrel-files/index.js +1179 -0
- package/dist/es2019/rules/import/no-relative-barrel-file-imports/index.js +738 -0
- package/dist/es2019/rules/import/shared/barrel-parsing.js +433 -0
- package/dist/es2019/rules/import/shared/file-system.js +174 -0
- package/dist/es2019/rules/import/shared/jest-utils.js +159 -0
- package/dist/es2019/rules/import/shared/package-registry.js +240 -0
- package/dist/es2019/rules/import/shared/package-resolution.js +161 -0
- package/dist/es2019/rules/import/shared/perf.js +83 -0
- package/dist/es2019/rules/import/shared/types.js +57 -0
- package/dist/esm/index.js +14 -3
- package/dist/esm/rules/feature-gating/valid-gate-name/index.js +53 -0
- package/dist/esm/rules/import/no-barrel-entry-imports/index.js +864 -0
- package/dist/esm/rules/import/no-barrel-entry-jest-mock/index.js +1375 -0
- package/dist/esm/rules/import/no-conversation-assistant-barrel-imports/index.js +37 -0
- package/dist/esm/rules/import/no-jest-mock-barrel-files/index.js +1391 -0
- package/dist/esm/rules/import/no-relative-barrel-file-imports/index.js +770 -0
- package/dist/esm/rules/import/shared/barrel-parsing.js +500 -0
- package/dist/esm/rules/import/shared/file-system.js +176 -0
- package/dist/esm/rules/import/shared/jest-utils.js +179 -0
- package/dist/esm/rules/import/shared/package-registry.js +256 -0
- package/dist/esm/rules/import/shared/package-resolution.js +175 -0
- package/dist/esm/rules/import/shared/perf.js +80 -0
- package/dist/esm/rules/import/shared/types.js +61 -0
- package/dist/types/index.d.ts +16 -2
- package/dist/types/rules/feature-gating/valid-gate-name/index.d.ts +3 -0
- package/dist/types/rules/import/no-barrel-entry-imports/index.d.ts +9 -0
- package/dist/types/rules/import/no-barrel-entry-jest-mock/index.d.ts +9 -0
- package/dist/types/rules/import/no-conversation-assistant-barrel-imports/index.d.ts +3 -0
- package/dist/types/rules/import/no-jest-mock-barrel-files/index.d.ts +22 -0
- package/dist/types/rules/import/no-relative-barrel-file-imports/index.d.ts +5 -0
- package/dist/types/rules/import/shared/barrel-parsing.d.ts +30 -0
- package/dist/types/rules/import/shared/file-system.d.ts +38 -0
- package/dist/types/rules/import/shared/jest-utils.d.ts +47 -0
- package/dist/types/rules/import/shared/package-registry.d.ts +26 -0
- package/dist/types/rules/import/shared/package-resolution.d.ts +38 -0
- package/dist/types/rules/import/shared/perf.d.ts +13 -0
- package/dist/types/rules/import/shared/types.d.ts +131 -0
- package/dist/types-ts4.5/index.d.ts +16 -2
- package/dist/types-ts4.5/rules/import/no-barrel-entry-imports/index.d.ts +9 -0
- package/dist/types-ts4.5/rules/import/no-barrel-entry-jest-mock/index.d.ts +9 -0
- package/dist/types-ts4.5/rules/import/no-jest-mock-barrel-files/index.d.ts +22 -0
- package/dist/types-ts4.5/rules/import/no-relative-barrel-file-imports/index.d.ts +5 -0
- package/dist/types-ts4.5/rules/import/shared/barrel-parsing.d.ts +30 -0
- package/dist/types-ts4.5/rules/import/shared/file-system.d.ts +38 -0
- package/dist/types-ts4.5/rules/import/shared/jest-utils.d.ts +47 -0
- package/dist/types-ts4.5/rules/import/shared/package-registry.d.ts +26 -0
- package/dist/types-ts4.5/rules/import/shared/package-resolution.d.ts +38 -0
- package/dist/types-ts4.5/rules/import/shared/perf.d.ts +13 -0
- package/dist/types-ts4.5/rules/import/shared/types.d.ts +131 -0
- package/package.json +4 -2
- package/dist/cjs/rules/ensure-native-and-af-exports-synced/index.js +0 -158
- package/dist/es2019/rules/ensure-native-and-af-exports-synced/index.js +0 -146
- package/dist/esm/rules/ensure-native-and-af-exports-synced/index.js +0 -151
- /package/dist/types-ts4.5/rules/{ensure-native-and-af-exports-synced → feature-gating/valid-gate-name}/index.d.ts +0 -0
- /package/dist/{types/rules/ensure-native-and-af-exports-synced → types-ts4.5/rules/import/no-conversation-assistant-barrel-imports}/index.d.ts +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ExportInfo, FileSystem } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Get all named exports from a file.
|
|
4
|
+
* This extracts what names are exported from a file for star exports.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getNamedExportsFromFile({ filePath, fs, }: {
|
|
7
|
+
filePath: string;
|
|
8
|
+
fs: FileSystem;
|
|
9
|
+
}): Set<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Check if an export map represents a barrel file (has re-exports from other files)
|
|
12
|
+
*/
|
|
13
|
+
export declare function hasReExportsFromOtherFiles({ exportMap, sourceFilePath, }: {
|
|
14
|
+
exportMap: Map<string, ExportInfo>;
|
|
15
|
+
sourceFilePath: string;
|
|
16
|
+
}): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Parse export statements from a file to find where each export comes from.
|
|
19
|
+
* Returns a map of export name -> ExportInfo.
|
|
20
|
+
*
|
|
21
|
+
* This function recursively traces through nested barrels and cross-package re-exports
|
|
22
|
+
* to find the ultimate source file for each export.
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseBarrelExports({ barrelFilePath, depth, fs, workspaceRoot, visitedPackages, }: {
|
|
25
|
+
barrelFilePath: string;
|
|
26
|
+
depth?: number;
|
|
27
|
+
fs: FileSystem;
|
|
28
|
+
workspaceRoot?: string;
|
|
29
|
+
visitedPackages?: Set<string>;
|
|
30
|
+
}): Map<string, ExportInfo>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { FileSystem } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* The default folder paths that barrel import rules apply to.
|
|
4
|
+
* Only imports from packages within these folders will be checked.
|
|
5
|
+
* This can be overridden via lint rule options.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEFAULT_TARGET_FOLDERS: string[];
|
|
8
|
+
/**
|
|
9
|
+
* Try to read file contents with error handling.
|
|
10
|
+
* Returns null if file cannot be read.
|
|
11
|
+
*/
|
|
12
|
+
export declare function readFileContent({ filePath, fs, }: {
|
|
13
|
+
filePath: string;
|
|
14
|
+
fs: FileSystem;
|
|
15
|
+
}): string | null;
|
|
16
|
+
/**
|
|
17
|
+
* Check if a path is a relative import (starts with ./ or ../)
|
|
18
|
+
*/
|
|
19
|
+
export declare function isRelativeImport(importPath: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the actual file path from an import path.
|
|
22
|
+
* Handles extension inference and index file resolution.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveImportPath({ basedir, importPath, fs, }: {
|
|
25
|
+
basedir: string;
|
|
26
|
+
importPath: string;
|
|
27
|
+
fs: FileSystem;
|
|
28
|
+
}): string | null;
|
|
29
|
+
/**
|
|
30
|
+
* Find the workspace root using git rev-parse --show-toplevel.
|
|
31
|
+
* The result is cached on fs.cache.gitRepoRoot to avoid repeated shell calls.
|
|
32
|
+
* Falls back to directory traversal if git command fails.
|
|
33
|
+
*/
|
|
34
|
+
export declare function findWorkspaceRoot({ startPath, fs, applyToImportsFrom, }: {
|
|
35
|
+
startPath: string;
|
|
36
|
+
fs: FileSystem;
|
|
37
|
+
applyToImportsFrom?: string[];
|
|
38
|
+
}): string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { TSESTree } from '@typescript-eslint/utils';
|
|
2
|
+
/**
|
|
3
|
+
* Shared utilities for jest.mock-related lint rules.
|
|
4
|
+
*
|
|
5
|
+
* These helpers are used by both `no-barrel-entry-jest-mock` (cross-package)
|
|
6
|
+
* and `no-jest-mock-barrel-files` (relative imports).
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Check if a CallExpression node is a jest.mock() call
|
|
10
|
+
*/
|
|
11
|
+
export declare function isJestMockCall(node: TSESTree.CallExpression): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Check if a node is a jest.requireActual() call
|
|
14
|
+
*/
|
|
15
|
+
export declare function isJestRequireActual(node: TSESTree.Node): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Check if a node is a jest.requireMock() call
|
|
18
|
+
*/
|
|
19
|
+
export declare function isJestRequireMock(node: TSESTree.Node): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Extract the import path string from a jest.mock/jest.requireMock/jest.requireActual call's arguments.
|
|
22
|
+
* Returns null if the path cannot be statically determined.
|
|
23
|
+
*/
|
|
24
|
+
export declare function extractImportPath(node: TSESTree.CallExpression): string | null;
|
|
25
|
+
/**
|
|
26
|
+
* Find all jest.requireMock() calls in the AST whose import path matches a given target.
|
|
27
|
+
*
|
|
28
|
+
* The `matchPath` callback allows callers to provide their own path-matching strategy:
|
|
29
|
+
* - Cross-package rules can use simple string equality
|
|
30
|
+
* - Relative import rules can use normalized/resolved path comparison
|
|
31
|
+
*/
|
|
32
|
+
export declare function findJestRequireMockCalls({ ast, matchPath, }: {
|
|
33
|
+
ast: TSESTree.Program;
|
|
34
|
+
matchPath: (candidatePath: string) => boolean;
|
|
35
|
+
}): TSESTree.CallExpression[];
|
|
36
|
+
/**
|
|
37
|
+
* Determine the best new import path for a jest.requireMock() call by inspecting
|
|
38
|
+
* the destructured symbols or property access at the call site.
|
|
39
|
+
*
|
|
40
|
+
* @param requireMockNode - The jest.requireMock() CallExpression node
|
|
41
|
+
* @param symbolToNewPath - Map from symbol name to the new mock path that provides it
|
|
42
|
+
* @returns The resolved new path, or null if it cannot be determined
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveNewPathForRequireMock({ requireMockNode, symbolToNewPath, }: {
|
|
45
|
+
requireMockNode: TSESTree.CallExpression;
|
|
46
|
+
symbolToNewPath: Map<string, string>;
|
|
47
|
+
}): string | null;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FileSystem } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Find the package directory for a given package name.
|
|
4
|
+
* Returns the absolute path to the package directory or null if not found.
|
|
5
|
+
*
|
|
6
|
+
* This function uses lazy scanning - it will scan platform/packages on first lookup
|
|
7
|
+
* and cache results in fs.cache for subsequent lookups.
|
|
8
|
+
*
|
|
9
|
+
* Note: Package resolution is NOT constrained by applyToImportsFrom. Any package under
|
|
10
|
+
* platform/packages can be resolved. Use isPackageInApplyToImportsFrom to check if a
|
|
11
|
+
* package should be processed by the lint rule.
|
|
12
|
+
*/
|
|
13
|
+
export declare function findPackageInRegistry({ packageName, workspaceRoot, fs, }: {
|
|
14
|
+
packageName: string;
|
|
15
|
+
workspaceRoot: string;
|
|
16
|
+
fs: FileSystem;
|
|
17
|
+
}): string | null;
|
|
18
|
+
/**
|
|
19
|
+
* Check if a package is within one of the applyToImportsFrom folders.
|
|
20
|
+
* This can be used to quickly filter out packages that shouldn't be checked.
|
|
21
|
+
*/
|
|
22
|
+
export declare function isPackageInApplyToImportsFrom({ packageDir, workspaceRoot, applyToImportsFrom, }: {
|
|
23
|
+
packageDir: string;
|
|
24
|
+
workspaceRoot: string;
|
|
25
|
+
applyToImportsFrom?: string[];
|
|
26
|
+
}): boolean;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { FileSystem } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Parse the package.json exports field and return a map of export paths to resolved file paths.
|
|
4
|
+
*/
|
|
5
|
+
export declare function parsePackageExports({ packageDir, fs, }: {
|
|
6
|
+
packageDir: string;
|
|
7
|
+
fs: FileSystem;
|
|
8
|
+
}): Map<string, string>;
|
|
9
|
+
/**
|
|
10
|
+
* Find a matching export entry for a given source file path.
|
|
11
|
+
* Returns the export path (e.g., "./controllers/analytics") or null if not found.
|
|
12
|
+
*/
|
|
13
|
+
export declare function findExportForSourceFile({ sourceFilePath, exportsMap, }: {
|
|
14
|
+
sourceFilePath: string;
|
|
15
|
+
exportsMap: Map<string, string>;
|
|
16
|
+
}): string | null;
|
|
17
|
+
/**
|
|
18
|
+
* Extract the package name and subpath from an import specifier.
|
|
19
|
+
* Returns null if the import is not a scoped package import.
|
|
20
|
+
*/
|
|
21
|
+
export declare function extractPackageNameFromImport(moduleSpecifier: string): {
|
|
22
|
+
packageName: string;
|
|
23
|
+
subPath: string;
|
|
24
|
+
} | null;
|
|
25
|
+
/**
|
|
26
|
+
* Resolve a cross-package import to its package directory and export info.
|
|
27
|
+
* Returns null if the package is not in the target folder or cannot be resolved.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveCrossPackageImport({ moduleSpecifier, workspaceRoot, fs, }: {
|
|
30
|
+
moduleSpecifier: string;
|
|
31
|
+
workspaceRoot: string;
|
|
32
|
+
fs: FileSystem;
|
|
33
|
+
}): {
|
|
34
|
+
packageName: string;
|
|
35
|
+
packageDir: string;
|
|
36
|
+
exportPath: string;
|
|
37
|
+
entryFilePath: string;
|
|
38
|
+
} | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FileSystem } from './types';
|
|
2
|
+
export declare const PERF_ENV_VAR = "INTERNAL_ESLINT_BARREL_PERF";
|
|
3
|
+
export declare function isPerfEnabled(): boolean;
|
|
4
|
+
export declare function perfInc({ fs, key, by }: {
|
|
5
|
+
fs: FileSystem;
|
|
6
|
+
key: string;
|
|
7
|
+
by?: number;
|
|
8
|
+
}): void;
|
|
9
|
+
export declare function perfTime<T>({ fs, key, fn }: {
|
|
10
|
+
fs: FileSystem;
|
|
11
|
+
key: string;
|
|
12
|
+
fn: () => T;
|
|
13
|
+
}): T;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Directory entry returned by readdirSync with withFileTypes option.
|
|
3
|
+
*/
|
|
4
|
+
export interface DirectoryEntry {
|
|
5
|
+
name: string;
|
|
6
|
+
isDirectory(): boolean;
|
|
7
|
+
isFile(): boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* State for the package registry cache.
|
|
11
|
+
* This is used to cache package name to directory mappings for efficient lookups.
|
|
12
|
+
*/
|
|
13
|
+
export interface PackageRegistryCache {
|
|
14
|
+
/** Map of package name to absolute directory path */
|
|
15
|
+
packageNameToDir: Map<string, string>;
|
|
16
|
+
/** Set of directories that have been scanned (including those without packages) */
|
|
17
|
+
scannedDirectories: Set<string>;
|
|
18
|
+
/** yarn.lock mtime when cache was built (for invalidation) */
|
|
19
|
+
yarnLockMtime: number;
|
|
20
|
+
/** The workspace root this cache was built for (from package registry perspective) */
|
|
21
|
+
workspaceRoot: string | null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Cache structure for file system operations.
|
|
25
|
+
* Contains both package registry cache and workspace root cache.
|
|
26
|
+
*/
|
|
27
|
+
export interface FileSystemCache extends Partial<PackageRegistryCache> {
|
|
28
|
+
/** Cached git repository root (from git rev-parse --show-toplevel) */
|
|
29
|
+
gitRepoRoot?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Cache of parsed package.json exports maps keyed by absolute package directory.
|
|
32
|
+
* This avoids repeated reads/parses during IDE lint runs.
|
|
33
|
+
*/
|
|
34
|
+
packageExportsByDir?: Map<string, {
|
|
35
|
+
/** mtimeMs of package.json when this entry was computed. null means unknown (forces re-read). */
|
|
36
|
+
packageJsonMtimeMs: number | null;
|
|
37
|
+
exportsMap: Map<string, string>;
|
|
38
|
+
}>;
|
|
39
|
+
/**
|
|
40
|
+
* Cache of read file contents keyed by absolute file path.
|
|
41
|
+
* Used by barrel parsing to avoid repeated reads in IDE lint runs.
|
|
42
|
+
*/
|
|
43
|
+
fileContentByPath?: Map<string, {
|
|
44
|
+
/** mtimeMs when this entry was cached. null means unknown (forces re-read). */
|
|
45
|
+
mtimeMs: number | null;
|
|
46
|
+
content: string;
|
|
47
|
+
}>;
|
|
48
|
+
/**
|
|
49
|
+
* Cache of parsed barrel exports keyed by barrel file path.
|
|
50
|
+
* Stores the barrel file mtime at time of parsing to support invalidation.
|
|
51
|
+
*/
|
|
52
|
+
barrelExportsByPath?: Map<string, {
|
|
53
|
+
mtimeMs: number;
|
|
54
|
+
exports: Map<string, ExportInfo>;
|
|
55
|
+
}>;
|
|
56
|
+
/**
|
|
57
|
+
* Cache for resolveImportPath results keyed by basedir + importPath.
|
|
58
|
+
*/
|
|
59
|
+
resolvedImportPathByKey?: Map<string, string | null>;
|
|
60
|
+
/**
|
|
61
|
+
* Optional perf counters and timers for debugging and optimization.
|
|
62
|
+
* Enabled by setting INTERNAL_ESLINT_BARREL_PERF.
|
|
63
|
+
*/
|
|
64
|
+
perf?: {
|
|
65
|
+
installedExitHook: boolean;
|
|
66
|
+
counters: Record<string, number>;
|
|
67
|
+
timers: Record<string, number>;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* File system abstraction for testability.
|
|
72
|
+
* This interface allows the core logic to be tested with mock file systems.
|
|
73
|
+
* The cache property holds package resolution state and can be passed as an empty
|
|
74
|
+
* object for tests to ensure fresh state for each test case.
|
|
75
|
+
*/
|
|
76
|
+
export interface FileSystem {
|
|
77
|
+
existsSync(path: string): boolean;
|
|
78
|
+
readFileSync(path: string, encoding: 'utf-8'): string;
|
|
79
|
+
realpathSync(path: string): string;
|
|
80
|
+
statSync(path: string): {
|
|
81
|
+
isFile(): boolean;
|
|
82
|
+
mtimeMs?: number;
|
|
83
|
+
};
|
|
84
|
+
readdirSync(path: string, options: {
|
|
85
|
+
withFileTypes: true;
|
|
86
|
+
}): DirectoryEntry[];
|
|
87
|
+
/** Execute a command synchronously and return stdout. Returns null on error. */
|
|
88
|
+
execSync(command: string, options?: {
|
|
89
|
+
cwd?: string;
|
|
90
|
+
}): string | null;
|
|
91
|
+
/** Cache for package resolution and workspace root - will be populated lazily */
|
|
92
|
+
cache: FileSystemCache;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Real file system implementation using Node.js fs module.
|
|
96
|
+
*/
|
|
97
|
+
export declare const realFileSystem: FileSystem;
|
|
98
|
+
/**
|
|
99
|
+
* Information about cross-package re-export origin.
|
|
100
|
+
*/
|
|
101
|
+
export interface CrossPackageSource {
|
|
102
|
+
/** The package name (e.g., '@atlassian/package-b') */
|
|
103
|
+
packageName: string;
|
|
104
|
+
/** The export path within the package (e.g., '.' or './utils') */
|
|
105
|
+
exportPath: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Information about where an export originates.
|
|
109
|
+
*/
|
|
110
|
+
export interface ExportInfo {
|
|
111
|
+
/** The absolute path to the file where this export originates */
|
|
112
|
+
path: string;
|
|
113
|
+
/** Whether this is a type-only export */
|
|
114
|
+
isTypeOnly: boolean;
|
|
115
|
+
/** Whether this is a re-export of a default export */
|
|
116
|
+
isDefaultExport?: boolean;
|
|
117
|
+
/** The original name of the symbol in the source file (for aliased exports) */
|
|
118
|
+
originalName?: string;
|
|
119
|
+
/** Information about cross-package re-export origin, if applicable */
|
|
120
|
+
crossPackageSource?: CrossPackageSource;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Context for package resolution operations.
|
|
124
|
+
*/
|
|
125
|
+
export interface PackageResolutionContext {
|
|
126
|
+
packageName: string;
|
|
127
|
+
packageDir: string;
|
|
128
|
+
exportPath: string;
|
|
129
|
+
entryFilePath: string;
|
|
130
|
+
exportsMap: Map<string, string>;
|
|
131
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/eslint-plugin-platform",
|
|
3
3
|
"description": "The essential plugin for use with Atlassian frontend platform tools",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.8.0",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"atlassian": {
|
|
7
7
|
"team": "Build Infra",
|
|
@@ -36,9 +36,11 @@
|
|
|
36
36
|
"@manypkg/find-root": "^1.1.0",
|
|
37
37
|
"@manypkg/get-packages": "^1.1.3",
|
|
38
38
|
"fuse.js": "^6.6.2",
|
|
39
|
-
"read-pkg-up": "^7.0.1"
|
|
39
|
+
"read-pkg-up": "^7.0.1",
|
|
40
|
+
"typescript": "5.9.2"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
43
|
+
"@atlassian/eslint-utils": "^0.5.0",
|
|
42
44
|
"@atlassian/ts-loader": "^0.1.0",
|
|
43
45
|
"@types/eslint": "^8.56.6",
|
|
44
46
|
"eslint": "^8.57.0",
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
Object.defineProperty(exports, "__esModule", {
|
|
5
|
-
value: true
|
|
6
|
-
});
|
|
7
|
-
exports.default = void 0;
|
|
8
|
-
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
|
|
9
|
-
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
10
|
-
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
11
|
-
var _path = _interopRequireDefault(require("path"));
|
|
12
|
-
var _registrationUtils = require("../util/registration-utils");
|
|
13
|
-
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
14
|
-
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
15
|
-
var exportsValidationExceptions = {
|
|
16
|
-
'@af/yarn-workspace': {
|
|
17
|
-
ignoredAfExportKeys: ['./lock-parser']
|
|
18
|
-
},
|
|
19
|
-
'@atlaskit/tokens': {
|
|
20
|
-
ignoredAfExportKeys: ['./babel-plugin']
|
|
21
|
-
},
|
|
22
|
-
'@atlaskit/storybook-addon-design-system': {
|
|
23
|
-
ignoredAfExportKeys: ['.']
|
|
24
|
-
},
|
|
25
|
-
'@atlassian/react-async': {
|
|
26
|
-
ignoredAfExportKeys: ['./mock']
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
var rule = {
|
|
30
|
-
meta: {
|
|
31
|
-
docs: {
|
|
32
|
-
recommended: false
|
|
33
|
-
},
|
|
34
|
-
type: 'problem',
|
|
35
|
-
messages: {
|
|
36
|
-
missingExportsProperty: "The exports property must be defined for {{pkgName}}; it most likely can just be a duplicate of the \"af:exports\" property. See http://go/eslint-exports for details",
|
|
37
|
-
missingExportsKey: "Missing package.json exports key \"{{expectedKey}}\" in {{pkgName}}. The exports entry should be \"{{expectedKey}}\": \"{{expectedValue}}\". See http://go/eslint-exports for details",
|
|
38
|
-
unexpectedExportsKey: "Unexpected package.json exports key \"{{key}}\" in {{pkgName}}. The exports entry should be \"{{expectedKey}}\": \"{{expectedValue}}\". See http://go/eslint-exports for details",
|
|
39
|
-
unexpectedExportsValue: "Unexpected package.json exports value in {{pkgName}} for the \"{{key}}\" key. The exports entry should be \"{{key}}\": \"{{expectedValue}}\". See http://go/eslint-exports for details"
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
create: function create(context) {
|
|
43
|
-
var fileName = context.getFilename();
|
|
44
|
-
if (!fileName.endsWith('package.json')) {
|
|
45
|
-
return {};
|
|
46
|
-
}
|
|
47
|
-
var _getMetadataForFilena = (0, _registrationUtils.getMetadataForFilename)(fileName),
|
|
48
|
-
packageJson = _getMetadataForFilena.pkgJson;
|
|
49
|
-
var pkgName = packageJson.name;
|
|
50
|
-
if (!pkgName || !packageJson['af:exports']) {
|
|
51
|
-
return {};
|
|
52
|
-
}
|
|
53
|
-
if (!packageJson['exports']) {
|
|
54
|
-
context.report({
|
|
55
|
-
node: context.getSourceCode().ast,
|
|
56
|
-
messageId: 'missingExportsProperty',
|
|
57
|
-
data: {
|
|
58
|
-
pkgName: pkgName
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
return {};
|
|
62
|
-
}
|
|
63
|
-
var afExports = packageJson['af:exports'];
|
|
64
|
-
var nativeExports = packageJson['exports'];
|
|
65
|
-
return {
|
|
66
|
-
Program: function Program(node) {
|
|
67
|
-
for (var _i = 0, _Object$entries = Object.entries(afExports); _i < _Object$entries.length; _i++) {
|
|
68
|
-
var _exportsValidationExc;
|
|
69
|
-
var _Object$entries$_i = (0, _slicedToArray2.default)(_Object$entries[_i], 2),
|
|
70
|
-
afExportsKey = _Object$entries$_i[0],
|
|
71
|
-
afExportsValue = _Object$entries$_i[1];
|
|
72
|
-
if ((_exportsValidationExc = exportsValidationExceptions[pkgName]) !== null && _exportsValidationExc !== void 0 && _exportsValidationExc.ignoredAfExportKeys.includes(afExportsKey)) {
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
var exportKeyViolations = getExportKeyViolation(afExportsKey, afExportsValue, nativeExports);
|
|
76
|
-
if (exportKeyViolations) {
|
|
77
|
-
context.report({
|
|
78
|
-
data: _objectSpread(_objectSpread({}, exportKeyViolations), {}, {
|
|
79
|
-
key: afExportsKey,
|
|
80
|
-
pkgName: pkgName
|
|
81
|
-
}),
|
|
82
|
-
node: node,
|
|
83
|
-
messageId: exportKeyViolations.messageId
|
|
84
|
-
});
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
var exportValueViolations = getExportValueViolation(afExportsKey, afExportsValue, nativeExports);
|
|
88
|
-
if (exportValueViolations) {
|
|
89
|
-
context.report({
|
|
90
|
-
data: _objectSpread(_objectSpread({}, exportValueViolations), {}, {
|
|
91
|
-
pkgName: pkgName
|
|
92
|
-
}),
|
|
93
|
-
node: node,
|
|
94
|
-
messageId: 'unexpectedExportsValue'
|
|
95
|
-
});
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
function getExportKeyViolation(afExportsKey, afExportsValue, nativeExports) {
|
|
104
|
-
var afExportsValueHasExtension = _path.default.extname(afExportsValue) !== '';
|
|
105
|
-
if (afExportsValueHasExtension && !nativeExports.hasOwnProperty(afExportsKey)) {
|
|
106
|
-
return {
|
|
107
|
-
messageId: 'missingExportsKey',
|
|
108
|
-
expectedKey: afExportsKey,
|
|
109
|
-
expectedValue: afExportsValue
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
if (!afExportsValueHasExtension && nativeExports.hasOwnProperty(afExportsKey)) {
|
|
113
|
-
return {
|
|
114
|
-
messageId: 'unexpectedExportsKey',
|
|
115
|
-
expectedKey: "".concat(afExportsKey, "/*"),
|
|
116
|
-
expectedValue: "".concat(afExportsValue, "/*")
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
if (!afExportsValueHasExtension && !nativeExports.hasOwnProperty("".concat(afExportsKey, "/*"))) {
|
|
120
|
-
return {
|
|
121
|
-
messageId: 'missingExportsKey',
|
|
122
|
-
expectedKey: "".concat(afExportsKey, "/*"),
|
|
123
|
-
expectedValue: "".concat(afExportsValue, "/*")
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
function getNativeExportsValue(afExportsKey, afExportsValueHasExtension, nativeExports) {
|
|
128
|
-
var nativeExportsKey = afExportsValueHasExtension ? afExportsKey : "".concat(afExportsKey, "/*");
|
|
129
|
-
if ((0, _typeof2.default)(nativeExports[nativeExportsKey]) === 'object') {
|
|
130
|
-
return nativeExports[nativeExportsKey].default;
|
|
131
|
-
}
|
|
132
|
-
return nativeExports[nativeExportsKey];
|
|
133
|
-
}
|
|
134
|
-
function getExportValueViolation(afExportsKey, afExportsValue, nativeExports) {
|
|
135
|
-
var afExportsValueHasExtension = _path.default.extname(afExportsValue) !== '';
|
|
136
|
-
var nativeExportsValue = getNativeExportsValue(afExportsKey, afExportsValueHasExtension, nativeExports);
|
|
137
|
-
|
|
138
|
-
// Some entrypoints have been updated to an index.js file that registers ts-node
|
|
139
|
-
// Use path.basename to get the file name to see if it is equal to 'index.js'
|
|
140
|
-
if (afExportsValueHasExtension && _path.default.basename(nativeExportsValue) === 'index.js') {
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
if (afExportsValueHasExtension && nativeExportsValue !== afExportsValue) {
|
|
144
|
-
return {
|
|
145
|
-
key: afExportsKey,
|
|
146
|
-
expectedValue: afExportsValue
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// af:exports entrypoints without a file extension export the whole directory so check to ensure the exports value includes the wildcard
|
|
151
|
-
if (!afExportsValueHasExtension && !nativeExportsValue.startsWith("".concat(afExportsValue, "/*"))) {
|
|
152
|
-
return {
|
|
153
|
-
key: "".concat(afExportsKey, "/*"),
|
|
154
|
-
expectedValue: "".concat(afExportsValue, "/*")
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
var _default = exports.default = rule;
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { getMetadataForFilename } from '../util/registration-utils';
|
|
3
|
-
const exportsValidationExceptions = {
|
|
4
|
-
'@af/yarn-workspace': {
|
|
5
|
-
ignoredAfExportKeys: ['./lock-parser']
|
|
6
|
-
},
|
|
7
|
-
'@atlaskit/tokens': {
|
|
8
|
-
ignoredAfExportKeys: ['./babel-plugin']
|
|
9
|
-
},
|
|
10
|
-
'@atlaskit/storybook-addon-design-system': {
|
|
11
|
-
ignoredAfExportKeys: ['.']
|
|
12
|
-
},
|
|
13
|
-
'@atlassian/react-async': {
|
|
14
|
-
ignoredAfExportKeys: ['./mock']
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
const rule = {
|
|
18
|
-
meta: {
|
|
19
|
-
docs: {
|
|
20
|
-
recommended: false
|
|
21
|
-
},
|
|
22
|
-
type: 'problem',
|
|
23
|
-
messages: {
|
|
24
|
-
missingExportsProperty: `The exports property must be defined for {{pkgName}}; it most likely can just be a duplicate of the "af:exports" property. See http://go/eslint-exports for details`,
|
|
25
|
-
missingExportsKey: `Missing package.json exports key "{{expectedKey}}" in {{pkgName}}. The exports entry should be "{{expectedKey}}": "{{expectedValue}}". See http://go/eslint-exports for details`,
|
|
26
|
-
unexpectedExportsKey: `Unexpected package.json exports key "{{key}}" in {{pkgName}}. The exports entry should be "{{expectedKey}}": "{{expectedValue}}". See http://go/eslint-exports for details`,
|
|
27
|
-
unexpectedExportsValue: `Unexpected package.json exports value in {{pkgName}} for the "{{key}}" key. The exports entry should be "{{key}}": "{{expectedValue}}". See http://go/eslint-exports for details`
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
create(context) {
|
|
31
|
-
const fileName = context.getFilename();
|
|
32
|
-
if (!fileName.endsWith('package.json')) {
|
|
33
|
-
return {};
|
|
34
|
-
}
|
|
35
|
-
const {
|
|
36
|
-
pkgJson: packageJson
|
|
37
|
-
} = getMetadataForFilename(fileName);
|
|
38
|
-
const pkgName = packageJson.name;
|
|
39
|
-
if (!pkgName || !packageJson['af:exports']) {
|
|
40
|
-
return {};
|
|
41
|
-
}
|
|
42
|
-
if (!packageJson['exports']) {
|
|
43
|
-
context.report({
|
|
44
|
-
node: context.getSourceCode().ast,
|
|
45
|
-
messageId: 'missingExportsProperty',
|
|
46
|
-
data: {
|
|
47
|
-
pkgName
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
return {};
|
|
51
|
-
}
|
|
52
|
-
const afExports = packageJson['af:exports'];
|
|
53
|
-
const nativeExports = packageJson['exports'];
|
|
54
|
-
return {
|
|
55
|
-
Program(node) {
|
|
56
|
-
for (const [afExportsKey, afExportsValue] of Object.entries(afExports)) {
|
|
57
|
-
var _exportsValidationExc;
|
|
58
|
-
if ((_exportsValidationExc = exportsValidationExceptions[pkgName]) !== null && _exportsValidationExc !== void 0 && _exportsValidationExc.ignoredAfExportKeys.includes(afExportsKey)) {
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
const exportKeyViolations = getExportKeyViolation(afExportsKey, afExportsValue, nativeExports);
|
|
62
|
-
if (exportKeyViolations) {
|
|
63
|
-
context.report({
|
|
64
|
-
data: {
|
|
65
|
-
...exportKeyViolations,
|
|
66
|
-
key: afExportsKey,
|
|
67
|
-
pkgName
|
|
68
|
-
},
|
|
69
|
-
node,
|
|
70
|
-
messageId: exportKeyViolations.messageId
|
|
71
|
-
});
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
const exportValueViolations = getExportValueViolation(afExportsKey, afExportsValue, nativeExports);
|
|
75
|
-
if (exportValueViolations) {
|
|
76
|
-
context.report({
|
|
77
|
-
data: {
|
|
78
|
-
...exportValueViolations,
|
|
79
|
-
pkgName
|
|
80
|
-
},
|
|
81
|
-
node,
|
|
82
|
-
messageId: 'unexpectedExportsValue'
|
|
83
|
-
});
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
function getExportKeyViolation(afExportsKey, afExportsValue, nativeExports) {
|
|
92
|
-
const afExportsValueHasExtension = path.extname(afExportsValue) !== '';
|
|
93
|
-
if (afExportsValueHasExtension && !nativeExports.hasOwnProperty(afExportsKey)) {
|
|
94
|
-
return {
|
|
95
|
-
messageId: 'missingExportsKey',
|
|
96
|
-
expectedKey: afExportsKey,
|
|
97
|
-
expectedValue: afExportsValue
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
if (!afExportsValueHasExtension && nativeExports.hasOwnProperty(afExportsKey)) {
|
|
101
|
-
return {
|
|
102
|
-
messageId: 'unexpectedExportsKey',
|
|
103
|
-
expectedKey: `${afExportsKey}/*`,
|
|
104
|
-
expectedValue: `${afExportsValue}/*`
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
if (!afExportsValueHasExtension && !nativeExports.hasOwnProperty(`${afExportsKey}/*`)) {
|
|
108
|
-
return {
|
|
109
|
-
messageId: 'missingExportsKey',
|
|
110
|
-
expectedKey: `${afExportsKey}/*`,
|
|
111
|
-
expectedValue: `${afExportsValue}/*`
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function getNativeExportsValue(afExportsKey, afExportsValueHasExtension, nativeExports) {
|
|
116
|
-
const nativeExportsKey = afExportsValueHasExtension ? afExportsKey : `${afExportsKey}/*`;
|
|
117
|
-
if (typeof nativeExports[nativeExportsKey] === 'object') {
|
|
118
|
-
return nativeExports[nativeExportsKey].default;
|
|
119
|
-
}
|
|
120
|
-
return nativeExports[nativeExportsKey];
|
|
121
|
-
}
|
|
122
|
-
function getExportValueViolation(afExportsKey, afExportsValue, nativeExports) {
|
|
123
|
-
const afExportsValueHasExtension = path.extname(afExportsValue) !== '';
|
|
124
|
-
const nativeExportsValue = getNativeExportsValue(afExportsKey, afExportsValueHasExtension, nativeExports);
|
|
125
|
-
|
|
126
|
-
// Some entrypoints have been updated to an index.js file that registers ts-node
|
|
127
|
-
// Use path.basename to get the file name to see if it is equal to 'index.js'
|
|
128
|
-
if (afExportsValueHasExtension && path.basename(nativeExportsValue) === 'index.js') {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
if (afExportsValueHasExtension && nativeExportsValue !== afExportsValue) {
|
|
132
|
-
return {
|
|
133
|
-
key: afExportsKey,
|
|
134
|
-
expectedValue: afExportsValue
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// af:exports entrypoints without a file extension export the whole directory so check to ensure the exports value includes the wildcard
|
|
139
|
-
if (!afExportsValueHasExtension && !nativeExportsValue.startsWith(`${afExportsValue}/*`)) {
|
|
140
|
-
return {
|
|
141
|
-
key: `${afExportsKey}/*`,
|
|
142
|
-
expectedValue: `${afExportsValue}/*`
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
export default rule;
|