mirror of
https://github.com/graphql-hive/console
synced 2026-04-21 14:37:17 +00:00
Fix missing dependencies between packages (#121)
Why? Few reasons: - tsup treats dependencies as external code and does not bundle them - without dependencies turborepo will always serve stale code when some of dependencies changed Moving internal dependencies to devDependencies makes tsup treat them as non-external and turborepo still keep tracks of relations
This commit is contained in:
parent
40263a552a
commit
86f5945aab
28 changed files with 323 additions and 43 deletions
|
|
@ -6,6 +6,7 @@ module.exports = {
|
|||
reportUnusedDisableDirectives: true,
|
||||
ignorePatterns: [
|
||||
'scripts',
|
||||
'rules',
|
||||
'out',
|
||||
'public',
|
||||
'packages/web/app/src/graphql/index.ts',
|
||||
|
|
@ -17,7 +18,7 @@ module.exports = {
|
|||
sourceType: 'module',
|
||||
},
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint', 'import'],
|
||||
plugins: ['@typescript-eslint', 'import', 'hive'],
|
||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', ignoreRestSiblings: true }],
|
||||
|
|
@ -36,11 +37,17 @@ module.exports = {
|
|||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
devDependencies: ['packages/services/storage/tools/*.js'],
|
||||
devDependencies: ['packages/services/storage/tools/*.js', 'packages/services/**'],
|
||||
optionalDependencies: false,
|
||||
},
|
||||
],
|
||||
'no-restricted-imports': ['error', { patterns: ['packages/*'] }],
|
||||
'hive/enforce-deps-in-dev': [
|
||||
'error',
|
||||
{
|
||||
scopes: ['@hive', '@graphql-hive'],
|
||||
ignored: ['packages/libraries/**', 'packages/web/**'],
|
||||
},
|
||||
],
|
||||
|
||||
// 🚨 The following rules needs to be fixed and was temporarily disabled to avoid printing warning
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
"turbo": "env-cmd --silent turbo run"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint-plugin-hive": "file:./rules",
|
||||
"@changesets/cli": "2.22.0",
|
||||
"@graphql-codegen/add": "3.1.1",
|
||||
"@graphql-codegen/cli": "2.6.2",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "@hive/api",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"graphql": "^16.0.0",
|
||||
|
|
@ -45,6 +45,9 @@
|
|||
"zod": "3.15.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hive/schema": "0.0.0",
|
||||
"@hive/tokens": "0.0.0",
|
||||
"@graphql-hive/core": "0.2.2",
|
||||
"@types/auth0": "2.34.2",
|
||||
"@types/ioredis": "4.28.7",
|
||||
"@types/lodash": "4.14.182",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"graphql": "16.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hive/service-common": "0.0.0",
|
||||
"fastify": "3.29.0",
|
||||
"esbuild": "0.14.39",
|
||||
"cross-undici-fetch": "0.1.27",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import { Response, Request, Headers, ReadableStream } from 'cross-undici-fetch';
|
||||
|
||||
globalThis.Response = Response;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"type": "module",
|
||||
"name": "@hive/rate-limit",
|
||||
"description": "A microservice for Hive SaaS, that exposes information about rate limits per given org/target.",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "tsup-node src/dev.ts --watch --format esm --target node16 --onSuccess 'node dist/dev.js' | pino-pretty --translateTime HH:MM:ss TT --ignore pid,hostname",
|
||||
|
|
@ -23,6 +23,8 @@
|
|||
"got": "12.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hive/service-common": "0.0.0",
|
||||
"@hive/storage": "0.0.0",
|
||||
"pino-pretty": "6.0.0"
|
||||
},
|
||||
"buildOptions": {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "@hive/schema",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"version": "0.2.1",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "tsup-node src/dev.ts --format esm --target node16 --watch --onSuccess 'node dist/dev.js' | pino-pretty --translateTime HH:MM:ss TT --ignore pid,hostname",
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
"ioredis": "4.28.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hive/service-common": "0.0.0",
|
||||
"@types/ioredis": "4.28.7",
|
||||
"pino-pretty": "6.0.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"type": "module",
|
||||
"private": true,
|
||||
"bin": "index.js",
|
||||
"version": "0.27.8",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "tsup-node src/dev.ts --format esm --target node16 --watch --onSuccess 'node dist/dev.js' | pino-pretty --translateTime HH:MM:ss TT --ignore pid,hostname",
|
||||
|
|
@ -25,6 +25,10 @@
|
|||
"reflect-metadata": "0.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphql-hive/client": "0.15.4",
|
||||
"@hive/api": "0.0.0",
|
||||
"@hive/service-common": "0.0.0",
|
||||
"@hive/storage": "0.0.0",
|
||||
"pino-pretty": "6.0.0",
|
||||
"@swc/core": "1.2.185"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"private": true,
|
||||
"type": "module",
|
||||
"name": "@hive/service-common",
|
||||
"version": "0.1.3",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fastify": "3.29.0",
|
||||
|
|
|
|||
|
|
@ -56,28 +56,31 @@ services:
|
|||
- 'stack'
|
||||
|
||||
zookeeper:
|
||||
image: confluentinc/cp-zookeeper:6.2.2
|
||||
image: confluentinc/cp-zookeeper:6.2.2-3-ubi8
|
||||
hostname: zookeeper
|
||||
networks:
|
||||
- 'stack'
|
||||
ports:
|
||||
- '2181:2181'
|
||||
environment:
|
||||
BUMP: 1
|
||||
ZOOKEEPER_CLIENT_PORT: 2181
|
||||
ZOOKEEPER_TICK_TIME: 2000
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 20000
|
||||
hard: 40000
|
||||
healthcheck:
|
||||
test: ['CMD', 'cub', 'zk-ready', '127.0.0.1:2181', '10']
|
||||
interval: 30s
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
start_period: 30s
|
||||
retries: 6
|
||||
start_period: 15s
|
||||
environment:
|
||||
ZOOKEEPER_CLIENT_PORT: 2181
|
||||
ZOOKEEPER_TICK_TIME: 2000
|
||||
volumes:
|
||||
- ./volumes/zookeeper/db:/var/lib/zookeeper/data
|
||||
- ./volumes/zookeeper/log:/var/lib/zookeeper/log
|
||||
|
||||
broker:
|
||||
image: confluentinc/cp-kafka:6.2.2
|
||||
image: confluentinc/cp-kafka:6.2.2-3-ubi8
|
||||
hostname: borker
|
||||
depends_on:
|
||||
zookeeper:
|
||||
|
|
@ -87,19 +90,20 @@ services:
|
|||
ports:
|
||||
- '29092:29092'
|
||||
- '9092:9092'
|
||||
healthcheck:
|
||||
test: ['CMD', 'cub', 'kafka-ready', '1', '5', '-b', '127.0.0.1:9092', '-c', '/etc/kafka/kafka.properties']
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
start_period: 30s
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 16384
|
||||
hard: 16384
|
||||
soft: 20000
|
||||
hard: 40000
|
||||
healthcheck:
|
||||
test: ['CMD', 'cub', 'kafka-ready', '1', '5', '-b', '127.0.0.1:9092', '-c', '/etc/kafka/kafka.properties']
|
||||
interval: 15s
|
||||
timeout: 10s
|
||||
retries: 6
|
||||
start_period: 15s
|
||||
environment:
|
||||
KAFKA_BROKER_ID: 1
|
||||
KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
|
||||
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
|
||||
KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
|
||||
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
|
||||
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://broker:29092,PLAINTEXT_HOST://localhost:9092
|
||||
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "@hive/storage",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"version": "0.14.1",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"setup": "yarn db:start && yarn db",
|
||||
|
|
|
|||
|
|
@ -13,8 +13,10 @@ import type {
|
|||
Alert,
|
||||
AuthProvider,
|
||||
OrganizationBilling,
|
||||
Storage,
|
||||
ProjectType,
|
||||
OrganizationType,
|
||||
} from '@hive/api';
|
||||
import { Storage, ProjectType, OrganizationType } from '@hive/api';
|
||||
import { sql, TaggedTemplateLiteralInvocationType } from 'slonik';
|
||||
import {
|
||||
commits,
|
||||
|
|
@ -97,7 +99,7 @@ export async function createStorage(connection: string): Promise<Storage> {
|
|||
schemaPush: parseInt(organization.limit_schema_push_monthly),
|
||||
},
|
||||
billingPlan: organization.plan_name,
|
||||
type: organization.type === 'PERSONAL' ? OrganizationType.PERSONAL : OrganizationType.REGULAR,
|
||||
type: (organization.type === 'PERSONAL' ? 'PERSONAL' : 'REGULAR') as OrganizationType,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -570,7 +572,7 @@ export async function createStorage(connection: string): Promise<Storage> {
|
|||
},
|
||||
async getMyOrganization({ user }) {
|
||||
const org = await pool.maybeOne<Slonik<organizations>>(
|
||||
sql`SELECT * FROM public.organizations WHERE user_id = ${user} AND type = ${OrganizationType.PERSONAL} LIMIT 1`
|
||||
sql`SELECT * FROM public.organizations WHERE user_id = ${user} AND type = ${'PERSONAL'} LIMIT 1`
|
||||
);
|
||||
|
||||
return org ? transformOrganization(org) : null;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"type": "module",
|
||||
"name": "@hive/stripe-billing",
|
||||
"description": "A microservice for Hive SaaS, that syncs usage information to Stripe (metered billing)",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "tsup-node src/dev.ts --format esm --target node16 --watch --onSuccess 'node dist/dev.js' | pino-pretty --translateTime HH:MM:ss TT --ignore pid,hostname",
|
||||
|
|
@ -24,6 +24,8 @@
|
|||
"got": "12.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hive/service-common": "0.0.0",
|
||||
"@hive/storage": "0.0.0",
|
||||
"pino-pretty": "6.0.0"
|
||||
},
|
||||
"buildOptions": {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "@hive/tokens",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"version": "0.6.8",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "tsup-node src/dev.ts --format esm --target node16 --watch --onSuccess 'node dist/dev.js' | pino-pretty --translateTime HH:MM:ss TT --ignore pid,hostname",
|
||||
|
|
@ -21,6 +21,8 @@
|
|||
"reflect-metadata": "0.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hive/service-common": "0.0.0",
|
||||
"@hive/storage": "0.0.0",
|
||||
"@types/ms": "0.7.31",
|
||||
"pino-pretty": "6.0.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@hive/usage-common",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graphql": "16.5.0"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"type": "module",
|
||||
"name": "@hive/usage-estimator",
|
||||
"description": "A microservice for Hive SaaS, that calculates and exposes usage information.",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "tsup-node src/dev.ts --format esm --target node16 --watch --onSuccess 'node dist/dev.js' | pino-pretty --translateTime HH:MM:ss TT --ignore pid,hostname",
|
||||
|
|
@ -21,6 +21,9 @@
|
|||
"got": "12.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hive/api": "0.0.0",
|
||||
"@hive/service-common": "0.0.0",
|
||||
"@hive/storage": "0.0.0",
|
||||
"pino-pretty": "6.0.0"
|
||||
},
|
||||
"buildOptions": {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"private": true,
|
||||
"type": "module",
|
||||
"name": "@hive/usage-ingestor",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "tsup-node src/dev.ts --watch --format esm --target node16 --onSuccess 'node dist/dev.js' | pino-pretty --translateTime HH:MM:ss TT --ignore pid,hostname",
|
||||
|
|
@ -22,6 +22,9 @@
|
|||
"tiny-lru": "8.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphql-hive/core": "0.2.2",
|
||||
"@hive/service-common": "0.0.0",
|
||||
"@hive/usage-common": "0.0.0",
|
||||
"pino-pretty": "6.0.0"
|
||||
},
|
||||
"buildOptions": {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "@hive/usage",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"version": "0.18.0",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "tsup-node src/dev.ts --format esm --target node16 --watch --onSuccess 'node dist/dev.js' | pino-pretty --translateTime HH:MM:ss TT --ignore pid,hostname",
|
||||
|
|
@ -21,6 +21,9 @@
|
|||
"tiny-lru": "8.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hive/usage-common": "0.0.0",
|
||||
"@hive/service-common": "0.0.0",
|
||||
"@hive/tokens": "0.0.0",
|
||||
"pino-pretty": "6.0.0"
|
||||
},
|
||||
"buildOptions": {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "@hive/webhooks",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"version": "0.1.4",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "tsup-node src/dev.ts --format esm --target node16 --watch --onSuccess 'node dist/dev.js' | pino-pretty --translateTime HH:MM:ss TT --ignore pid,hostname",
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
"p-timeout": "5.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hive/service-common": "0.0.0",
|
||||
"ioredis": "4.28.3",
|
||||
"copyfiles": "2.4.1",
|
||||
"pino-pretty": "6.0.0"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@hive/app",
|
||||
"private": true,
|
||||
"version": "0.16.2",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"start": "next start",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { useQuery } from 'urql';
|
|||
import { useUser } from '@/components/auth/AuthProvider';
|
||||
import { Avatar, Button, DropdownMenu, HiveLink } from '@/components/v2';
|
||||
import {
|
||||
AlertTriangleIcon,
|
||||
ArrowDownIcon,
|
||||
CalendarIcon,
|
||||
FileTextIcon,
|
||||
|
|
@ -15,7 +16,6 @@ import {
|
|||
PlusIcon,
|
||||
SettingsIcon,
|
||||
TrendingUpIcon,
|
||||
AlertTriangleIcon,
|
||||
} from '@/components/v2/icon';
|
||||
import { CreateOrganizationModal } from '@/components/v2/modals';
|
||||
import { MeDocument, OrganizationsDocument, OrganizationsQuery, OrganizationType } from '@/graphql';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { ForwardRefExoticComponent } from 'react';
|
||||
import { Root, Item } from '@radix-ui/react-toggle-group';
|
||||
import { Item, Root } from '@radix-ui/react-toggle-group';
|
||||
import clsx from 'clsx';
|
||||
|
||||
type PropsOf<T> = T extends ForwardRefExoticComponent<infer P> ? P : unknown;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@hive/docs",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"start": "next start",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@hive/landing-page",
|
||||
"private": true,
|
||||
"version": "0.1.20",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "bob runify --single"
|
||||
|
|
|
|||
227
rules/enforce-deps-in-dev.cjs
Normal file
227
rules/enforce-deps-in-dev.cjs
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
/**
|
||||
* Why? Few reasons:
|
||||
* - tsup treats dependencies as external code and does not bundle them
|
||||
* - without dependencies turborepo will always serve stale code when some of dependencies changed
|
||||
*
|
||||
* Moving internal dependencies to devDependencies makes tsup treat them as non-external and turborepo still keep tracks of relations
|
||||
*/
|
||||
|
||||
/// @ts-check
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const minimatch = require('minimatch');
|
||||
const readPkgUp = require('eslint-module-utils/readPkgUp').default;
|
||||
const moduleVisitor = require('eslint-module-utils/moduleVisitor').default;
|
||||
|
||||
function isHivePackage(packageName, scopes) {
|
||||
return typeof packageName === 'string' && scopes.some(scope => packageName.startsWith(`${scope}/`));
|
||||
}
|
||||
|
||||
const depFieldCache = new Map();
|
||||
|
||||
function hasKeys(obj = {}) {
|
||||
return Object.keys(obj).length > 0;
|
||||
}
|
||||
|
||||
function extractDepFields(pkg) {
|
||||
return {
|
||||
name: pkg.name,
|
||||
dependencies: pkg.dependencies || {},
|
||||
devDependencies: pkg.devDependencies || {},
|
||||
optionalDependencies: pkg.optionalDependencies || {},
|
||||
peerDependencies: pkg.peerDependencies || {},
|
||||
};
|
||||
}
|
||||
|
||||
function getDependencies(context, packageDir) {
|
||||
let paths = [];
|
||||
try {
|
||||
const packageContent = {
|
||||
name: '',
|
||||
dependencies: {},
|
||||
devDependencies: {},
|
||||
optionalDependencies: {},
|
||||
peerDependencies: {},
|
||||
};
|
||||
|
||||
if (packageDir && packageDir.length > 0) {
|
||||
if (!Array.isArray(packageDir)) {
|
||||
paths = [path.resolve(packageDir)];
|
||||
} else {
|
||||
paths = packageDir.map(dir => path.resolve(dir));
|
||||
}
|
||||
}
|
||||
|
||||
if (paths.length > 0) {
|
||||
// use rule config to find package.json
|
||||
paths.forEach(dir => {
|
||||
const packageJsonPath = path.join(dir, 'package.json');
|
||||
if (!depFieldCache.has(packageJsonPath)) {
|
||||
const depFields = extractDepFields(JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')));
|
||||
depFieldCache.set(packageJsonPath, depFields);
|
||||
}
|
||||
const _packageContent = depFieldCache.get(packageJsonPath);
|
||||
Object.keys(packageContent).forEach(depsKey =>
|
||||
Object.assign(packageContent[depsKey], _packageContent[depsKey])
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// use closest package.json
|
||||
Object.assign(
|
||||
packageContent,
|
||||
extractDepFields(
|
||||
readPkgUp({
|
||||
cwd: context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(),
|
||||
normalize: false,
|
||||
}).pkg
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
![
|
||||
packageContent.dependencies,
|
||||
packageContent.devDependencies,
|
||||
packageContent.optionalDependencies,
|
||||
packageContent.peerDependencies,
|
||||
].some(hasKeys)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return packageContent;
|
||||
} catch (e) {
|
||||
if (paths.length > 0 && e.code === 'ENOENT') {
|
||||
context.report({
|
||||
message: 'The package.json file could not be found.',
|
||||
loc: { line: 0, column: 0 },
|
||||
});
|
||||
}
|
||||
if (e.name === 'JSONError' || e instanceof SyntaxError) {
|
||||
context.report({
|
||||
message: 'The package.json file could not be parsed: ' + e.message,
|
||||
loc: { line: 0, column: 0 },
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function missingErrorMessage(packageName) {
|
||||
return `'${packageName}' should be listed in the project's devDependencies. `;
|
||||
}
|
||||
|
||||
function directDepErrorMessage(packageName) {
|
||||
return `'${packageName}' should be listed in the project's devDependencies, not dependencies.`;
|
||||
}
|
||||
|
||||
function optDepErrorMessage(packageName) {
|
||||
return `'${packageName}' should be listed in the project's devDependencies, not optionalDependencies`;
|
||||
}
|
||||
|
||||
function peerDepErrorMessage(packageName) {
|
||||
return `'${packageName}' should be listed in the project's devDependencies, not peerDependencies`;
|
||||
}
|
||||
|
||||
function getModuleOriginalName(name) {
|
||||
const [first, second] = name.split('/');
|
||||
return first.startsWith('@') ? `${first}/${second}` : first;
|
||||
}
|
||||
|
||||
function checkDependencyDeclaration(deps, packageName, declarationStatus) {
|
||||
const newDeclarationStatus = declarationStatus || {
|
||||
isInDeps: false,
|
||||
isInDevDeps: false,
|
||||
isInOptDeps: false,
|
||||
isInPeerDeps: false,
|
||||
};
|
||||
|
||||
// in case of sub package.json inside a module
|
||||
// check the dependencies on all hierarchy
|
||||
const packageHierarchy = [];
|
||||
const packageNameParts = packageName ? packageName.split('/') : [];
|
||||
packageNameParts.forEach((namePart, index) => {
|
||||
if (!namePart.startsWith('@')) {
|
||||
const ancestor = packageNameParts.slice(0, index + 1).join('/');
|
||||
packageHierarchy.push(ancestor);
|
||||
}
|
||||
});
|
||||
|
||||
return packageHierarchy.reduce((result, ancestorName) => {
|
||||
return {
|
||||
isInDeps: result.isInDeps || deps.dependencies[ancestorName] !== undefined,
|
||||
isInDevDeps: result.isInDevDeps || deps.devDependencies[ancestorName] !== undefined,
|
||||
isInOptDeps: result.isInOptDeps || deps.optionalDependencies[ancestorName] !== undefined,
|
||||
isInPeerDeps: result.isInPeerDeps || deps.peerDependencies[ancestorName] !== undefined,
|
||||
};
|
||||
}, newDeclarationStatus);
|
||||
}
|
||||
|
||||
function reportIfMissing(context, deps, node, name, scopes) {
|
||||
if (node.importKind === 'type' || node.importKind === 'typeof') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isHivePackage(name, scopes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const importPackageName = getModuleOriginalName(name);
|
||||
let declarationStatus = checkDependencyDeclaration(deps, importPackageName);
|
||||
|
||||
if (declarationStatus.isInDevDeps) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (declarationStatus.isInDeps) {
|
||||
context.report(node, directDepErrorMessage(importPackageName));
|
||||
return;
|
||||
}
|
||||
|
||||
if (declarationStatus.isInOptDeps) {
|
||||
context.report(node, optDepErrorMessage(importPackageName));
|
||||
return;
|
||||
}
|
||||
|
||||
if (declarationStatus.isInPeerDeps) {
|
||||
context.report(node, peerDepErrorMessage(importPackageName));
|
||||
return;
|
||||
}
|
||||
|
||||
context.report(node, missingErrorMessage(importPackageName));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const options = context.options[0] || {};
|
||||
const deps = getDependencies(context, options.packageDir) || extractDepFields({});
|
||||
|
||||
if (Array.isArray(options.ignored)) {
|
||||
const filepath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename();
|
||||
|
||||
if (
|
||||
options.ignored.some(
|
||||
ignored => minimatch(filepath, ignored) || minimatch(filepath, path.join(process.cwd(), ignored))
|
||||
)
|
||||
) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (!Array.isArray(options.scopes)) {
|
||||
throw new Error('[hive/enforce-deps-in-dev] The scopes option must be an array.');
|
||||
}
|
||||
|
||||
return moduleVisitor(
|
||||
(source, node) => {
|
||||
reportIfMissing(context, deps, node, source.value, options.scopes);
|
||||
},
|
||||
{ commonjs: true }
|
||||
);
|
||||
},
|
||||
};
|
||||
5
rules/index.cjs
Normal file
5
rules/index.cjs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
rules: {
|
||||
'enforce-deps-in-dev': require('./enforce-deps-in-dev.cjs'),
|
||||
},
|
||||
};
|
||||
7
rules/package.json
Normal file
7
rules/package.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"private": true,
|
||||
"name": "eslint-plugin-hive",
|
||||
"main": "index.cjs",
|
||||
"type": "commonjs",
|
||||
"version": "0.0.0"
|
||||
}
|
||||
|
|
@ -8236,6 +8236,9 @@ eslint-module-utils@^2.7.3:
|
|||
debug "^3.2.7"
|
||||
find-up "^2.1.0"
|
||||
|
||||
"eslint-plugin-hive@file:./rules":
|
||||
version "0.0.0"
|
||||
|
||||
eslint-plugin-import@2.26.0:
|
||||
version "2.26.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b"
|
||||
|
|
|
|||
Loading…
Reference in a new issue