0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2024-12-16 21:56:25 -05:00

refactor: relocate verdaccio-memory plugin (#1974)

This commit is contained in:
Juan Picado 2020-10-23 22:05:46 +02:00
parent 1d11128051
commit 827376e85c
15 changed files with 1813 additions and 20 deletions

View file

@ -0,0 +1,3 @@
{
"extends": "../../../.babelrc"
}

View file

@ -0,0 +1,5 @@
const config = require('../../../jest/config');
module.exports = Object.assign({}, config, {
collectCoverage: true,
});

View file

@ -0,0 +1,44 @@
{
"name": "verdaccio-memory",
"version": "10.0.0-beta",
"description": "Storage implementation in memory",
"keywords": [
"verdaccio",
"plugin",
"storage",
"memory"
],
"author": "Juan Picado <juanpicado19@gmail.com>",
"license": "MIT",
"homepage": "https://verdaccio.org",
"repository": {
"type": "git",
"url": "https://github.com/verdaccio/verdaccio",
"directory": "packages/plugins/memory"
},
"bugs": {
"url": "https://github.com/verdaccio/verdaccio/issues"
},
"main": "build/index.js",
"types": "build/index.d.ts",
"dependencies": {
"@verdaccio/commons-api": "workspace:*",
"@verdaccio/streams": "workspace:*",
"memory-fs": "^0.5.0"
},
"devDependencies": {
"@verdaccio/types": "workspace:*"
},
"scripts": {
"clean": "rimraf ./build",
"type-check": "tsc --noEmit -p tsconfig.build.json",
"build:types": "tsc --emitDeclarationOnly -p tsconfig.build.json",
"build:js": "babel src/ --out-dir build/ --copy-files --extensions \".ts,.tsx\" --source-maps",
"build": "pnpm run build:js && pnpm run build:types",
"test": "cross-env NODE_ENV=test BABEL_ENV=test jest"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/verdaccio"
}
}

View file

@ -0,0 +1,5 @@
import LocalMemory from './local-memory';
export { LocalMemory };
export default LocalMemory;

View file

@ -0,0 +1,124 @@
import { getServiceUnavailable } from '@verdaccio/commons-api';
import {
Logger,
Callback,
Config,
IPluginStorage,
Token,
TokenFilter,
PluginOptions,
} from '@verdaccio/types';
import MemoryHandler, { DataHandler } from './memory-handler';
export type ConfigMemory = Config & { limit?: number };
export interface MemoryLocalStorage {
secret: string;
list: string[];
files: DataHandler;
}
const DEFAULT_LIMIT = 1000;
class LocalMemory implements IPluginStorage<ConfigMemory> {
private path: string;
private limit: number;
public logger: Logger;
private data: MemoryLocalStorage;
public config: ConfigMemory;
public constructor(config: ConfigMemory, options: PluginOptions<ConfigMemory>) {
this.config = config;
this.limit = config.limit || DEFAULT_LIMIT;
this.logger = options.logger;
this.data = this._createEmtpyDatabase();
this.path = '/';
}
public getSecret(): Promise<string> {
return Promise.resolve(this.data.secret);
}
public setSecret(secret: string): Promise<string | null> {
return new Promise((resolve): void => {
this.data.secret = secret;
resolve(null);
});
}
public add(name: string, cb: Callback): void {
const { list } = this.data;
if (list.length < this.limit) {
if (list.indexOf(name) === -1) {
list.push(name);
}
cb(null);
} else {
this.logger.info(
{ limit: this.limit },
'Storage memory has reached limit of @{limit} packages'
);
cb(new Error('Storage memory has reached limit of limit packages'));
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public search(onPackage: Callback, onEnd: Callback, validateName: Function): void {
this.logger.warn('[verdaccio/memory]: search method not implemented, PR is welcome');
onEnd();
}
public remove(name: string, cb: Callback): void {
const { list } = this.data;
const item = list.indexOf(name);
if (item !== -1) {
list.splice(item, 1);
}
cb(null);
}
public get(cb: Callback): void {
cb(null, this.data.list);
}
public getPackageStorage(packageInfo: string): MemoryHandler {
return new MemoryHandler(packageInfo, this.data.files, this.logger);
}
private _createEmtpyDatabase(): MemoryLocalStorage {
const list: string[] = [];
const files = {};
const emptyDatabase = {
list,
files,
secret: '',
};
return emptyDatabase;
}
public saveToken(token: Token): Promise<void> {
this.logger.warn('[verdaccio/memory][saveToken] save token has not been implemented yet');
return Promise.reject(getServiceUnavailable('method not implemented'));
}
public deleteToken(user: string, tokenKey: string): Promise<void> {
this.logger.warn(
{ tokenKey, user },
'[verdaccio/memory][deleteToken] delete token has not been implemented yet @{user}'
);
return Promise.reject(getServiceUnavailable('method not implemented'));
}
public readTokens(filter: TokenFilter): Promise<Token[]> {
this.logger.warn('[verdaccio/memory][readTokens] read tokens has not been implemented yet ');
return Promise.reject(getServiceUnavailable('method not implemented'));
}
}
export default LocalMemory;

View file

@ -0,0 +1,190 @@
import {
VerdaccioError,
getBadRequest,
getInternalError,
getConflict,
getNotFound,
} from '@verdaccio/commons-api';
import MemoryFileSystem from 'memory-fs';
import { UploadTarball, ReadTarball } from '@verdaccio/streams';
import {
Callback,
Logger,
IPackageStorageManager,
IUploadTarball,
IReadTarball,
CallbackAction,
StorageUpdateCallback,
StorageWriteCallback,
PackageTransformer,
Package,
ReadPackageCallback,
} from '@verdaccio/types';
import { parsePackage, stringifyPackage } from './utils';
const fs = new MemoryFileSystem();
export type DataHandler = {
[key: string]: string;
};
class MemoryHandler implements IPackageStorageManager {
private data: DataHandler;
private name: string;
private path: string;
public logger: Logger;
public constructor(packageName: string, data: DataHandler, logger: Logger) {
// this is not need it
this.data = data;
this.name = packageName;
this.logger = logger;
this.path = '/';
}
public updatePackage(
pkgFileName: string,
updateHandler: StorageUpdateCallback,
onWrite: StorageWriteCallback,
transformPackage: PackageTransformer,
onEnd: CallbackAction
): void {
const json: string = this._getStorage(pkgFileName);
let pkg: Package;
try {
pkg = parsePackage(json) as Package;
} catch (err) {
return onEnd(err);
}
updateHandler(pkg, (err: VerdaccioError) => {
if (err) {
return onEnd(err);
}
try {
onWrite(pkgFileName, transformPackage(pkg), onEnd);
} catch (err) {
return onEnd(getInternalError('error on parse the metadata'));
}
});
}
public deletePackage(pkgName: string, callback: Callback): void {
delete this.data[pkgName];
return callback(null);
}
public removePackage(callback: CallbackAction): void {
return callback(null);
}
public createPackage(name: string, value: Package, cb: CallbackAction): void {
this.savePackage(name, value, cb);
}
public savePackage(name: string, value: Package, cb: CallbackAction): void {
try {
const json: string = stringifyPackage(value);
this.data[name] = json;
return cb(null);
} catch (err) {
return cb(getInternalError(err.message));
}
}
public readPackage(name: string, cb: ReadPackageCallback): void {
const json = this._getStorage(name);
const isJson = typeof json === 'undefined';
try {
return cb(isJson ? getNotFound() : null, parsePackage(json));
} catch (err) {
return cb(getNotFound());
}
}
public writeTarball(name: string): IUploadTarball {
const uploadStream: IUploadTarball = new UploadTarball({});
const temporalName = `/${name}`;
process.nextTick(function () {
fs.stat(temporalName, function (fileError, stats) {
if (!fileError && stats) {
return uploadStream.emit('error', getConflict());
}
try {
const file = fs.createWriteStream(temporalName);
uploadStream.pipe(file);
uploadStream.done = function (): void {
const onEnd = function (): void {
uploadStream.emit('success');
};
uploadStream.on('end', onEnd);
};
uploadStream.abort = function (): void {
uploadStream.emit('error', getBadRequest('transmision aborted'));
file.end();
};
uploadStream.emit('open');
return;
} catch (err) {
uploadStream.emit('error', err);
return;
}
});
});
return uploadStream;
}
public readTarball(name: string): IReadTarball {
const pathName = `/${name}`;
const readTarballStream: IReadTarball = new ReadTarball({});
process.nextTick(function () {
fs.stat(pathName, function (fileError, stats) {
if (fileError && !stats) {
return readTarballStream.emit('error', getNotFound());
}
try {
const readStream = fs.createReadStream(pathName);
const contentLength: number = (fs.data[name] && fs.data[name].length) || 0;
readTarballStream.emit('content-length', contentLength);
readTarballStream.emit('open');
readStream.pipe(readTarballStream);
readStream.on('error', (error: VerdaccioError) => {
readTarballStream.emit('error', error);
});
readTarballStream.abort = function (): void {
readStream.destroy(getBadRequest('read has been aborted'));
};
return;
} catch (err) {
readTarballStream.emit('error', err);
return;
}
});
});
return readTarballStream;
}
private _getStorage(name = ''): string {
return this.data[name];
}
}
export default MemoryHandler;

View file

@ -0,0 +1,9 @@
import { Package } from '@verdaccio/types';
export function stringifyPackage(pkg: Package) {
return JSON.stringify(pkg, null, '\t');
}
export function parsePackage(pkg: string) {
return JSON.parse(pkg);
}

View file

@ -0,0 +1,75 @@
import { Logger, IPluginStorage } from '@verdaccio/types';
import { VerdaccioError } from '@verdaccio/commons-api';
import { ConfigMemory } from '../src/local-memory';
import { DataHandler } from '../src/memory-handler';
import LocalMemory from '../src/index';
import config from './partials/config';
const logger: Logger = {
error: (e) => console.warn(e),
info: (e) => console.warn(e),
debug: (e) => console.warn(e),
child: (e) => console.warn(e),
warn: (e) => console.warn(e),
http: (e) => console.warn(e),
trace: (e) => console.warn(e),
};
const defaultConfig = { logger, config: null };
describe('memory unit test .', () => {
describe('LocalMemory', () => {
test('should create an LocalMemory instance', () => {
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
expect(localMemory).toBeDefined();
});
test('should create add a package', (done) => {
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
localMemory.add('test', (err: VerdaccioError) => {
expect(err).toBeNull();
localMemory.get((err: VerdaccioError, data: DataHandler) => {
expect(err).toBeNull();
expect(data).toHaveLength(1);
done();
});
});
});
test('should reach max limit', (done) => {
config.limit = 2;
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
localMemory.add('test1', (err) => {
expect(err).toBeNull();
localMemory.add('test2', (err) => {
expect(err).toBeNull();
localMemory.add('test3', (err) => {
expect(err).not.toBeNull();
expect(err.message).toMatch(/Storage memory has reached limit of limit packages/);
done();
});
});
});
});
test('should remove a package', (done) => {
const pkgName = 'test';
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
localMemory.add(pkgName, (err) => {
expect(err).toBeNull();
localMemory.remove(pkgName, (err) => {
expect(err).toBeNull();
localMemory.get((err, data) => {
expect(err).toBeNull();
expect(data).toHaveLength(0);
done();
});
});
});
});
});
});

View file

@ -0,0 +1,380 @@
import { Logger, IPluginStorage, IPackageStorage, ILocalPackageManager } from '@verdaccio/types';
import { getInternalError } from '@verdaccio/commons-api';
import { ConfigMemory } from '../src/local-memory';
import MemoryHandler from '../src/memory-handler';
import LocalMemory from '../src/index';
import config from './partials/config';
import pkgExample from './partials/pkg';
const logger: Logger = {
error: (e) => console.warn(e),
info: (e) => console.warn(e),
debug: (e) => console.warn(e),
child: (e) => console.warn(e),
warn: (e) => console.warn(e),
http: (e) => console.warn(e),
trace: (e) => console.warn(e),
};
const defaultConfig = { logger, config: null };
const mockStringify = jest.fn((value) => {
return jest.requireActual('../src/utils.ts').stringifyPackage(value);
});
const mockParsePackage = jest.fn((value) => {
return jest.requireActual('../src/utils.ts').parsePackage(value);
});
jest.mock('../src/utils.ts', () => ({
stringifyPackage: (value) => mockStringify(value),
parsePackage: (value) => mockParsePackage(value),
}));
describe('memory unit test .', () => {
describe('MemoryHandler', () => {
test('should create an MemoryHandler instance', () => {
const memoryHandler = new MemoryHandler(
'test',
{
['foo']: 'bar',
},
logger
);
expect(memoryHandler).toBeDefined();
});
test('should save a package', (done) => {
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
const pkgName = 'test';
const handler = localMemory.getPackageStorage(pkgName);
expect(handler).toBeDefined();
if (handler) {
handler.savePackage(pkgName, pkgExample, (err) => {
expect(err).toBeNull();
handler.readPackage(pkgName, (err, data) => {
expect(err).toBeNull();
expect(data).toEqual(pkgExample);
done();
});
});
}
});
test('should fails on save a package', (done) => {
mockStringify.mockImplementationOnce(() => {
throw new Error('error on parse');
});
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
const pkgName = 'test';
const handler: IPackageStorage = localMemory.getPackageStorage(
pkgName
) as ILocalPackageManager;
handler.savePackage(pkgName, pkgExample, (err) => {
expect(err).toEqual(getInternalError('error on parse'));
done();
});
});
test('should fails on read a package', (done) => {
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
const pkgName = 'test';
const handler = localMemory.getPackageStorage(pkgName);
expect(handler).toBeDefined();
if (handler) {
handler.readPackage(pkgName, (err) => {
expect(err).not.toBeNull();
expect(err.code).toBe(404);
done();
});
}
});
test('should update a package', (done) => {
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
const pkgName = 'test';
const handler = localMemory.getPackageStorage(pkgName);
expect(handler).toBeDefined();
const onEnd = jest.fn();
if (handler) {
handler.savePackage(pkgName, pkgExample, (err) => {
expect(err).toBeNull();
handler.updatePackage(
pkgName,
(json, callback) => {
expect(json).toBeDefined();
expect(json.name).toBe(pkgExample.name);
expect(callback).toBeDefined();
callback(null);
},
(name, data, onEnd) => {
expect(name).toBe(pkgName);
expect(data.name).toBe(pkgExample.name);
onEnd();
expect(onEnd).toHaveBeenCalled();
done();
},
(data) => {
expect(data).toBeDefined();
return data;
},
onEnd
);
});
}
});
test('should parse fails on update a package', (done) => {
mockParsePackage.mockImplementationOnce(() => {
throw new Error('error on parse');
});
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
const pkgName = 'test';
const handler = localMemory.getPackageStorage(pkgName);
expect(handler).toBeDefined();
const onEnd = jest.fn((err) => {
expect(err).not.toBeNull();
expect(err).toEqual(getInternalError('error on parse'));
done();
});
if (handler) {
handler.savePackage(pkgName, pkgExample, (err) => {
expect(err).toBeNull();
handler.updatePackage(
pkgName,
() => {},
() => {},
// @ts-ignore
() => {},
onEnd
);
});
}
});
test('should fail updateHandler update a package', (done) => {
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
const pkgName = 'test';
const handler = localMemory.getPackageStorage(pkgName);
expect(handler).toBeDefined();
const onEnd = jest.fn((err) => {
expect(err).not.toBeNull();
expect(err).toEqual(getInternalError('some error'));
done();
});
if (handler) {
handler.savePackage(pkgName, pkgExample, (err) => {
expect(err).toBeNull();
handler.updatePackage(
pkgName,
(json, callback) => {
expect(json).toBeDefined();
expect(json.name).toBe(pkgExample.name);
expect(callback).toBeDefined();
callback(getInternalError('some error'));
},
() => {},
// @ts-ignore
() => {},
onEnd
);
});
}
});
test('should onWrite update a package', (done) => {
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
const pkgName = 'test';
const handler = localMemory.getPackageStorage(pkgName);
expect(handler).toBeDefined();
const onEnd = jest.fn((err) => {
expect(err).not.toBeNull();
expect(err).toEqual(getInternalError('error on parse the metadata'));
done();
});
if (handler) {
handler.savePackage(pkgName, pkgExample, (err) => {
expect(err).toBeNull();
handler.updatePackage(
pkgName,
(json, callback) => {
expect(json).toBeDefined();
expect(json.name).toBe(pkgExample.name);
expect(callback).toBeDefined();
callback(null);
},
(name, data, onEnd) => {
expect(name).toBe(pkgName);
expect(data.name).toBe(pkgExample.name);
onEnd();
expect(onEnd).toHaveBeenCalled();
done();
},
() => {
throw new Error('dadsads');
},
onEnd
);
});
}
});
describe('writing/reading files', () => {
test('should write a tarball', (done) => {
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
const pkgName = 'test';
const dataTarball = '12345';
const handler = localMemory.getPackageStorage(pkgName);
if (handler) {
const stream = handler.writeTarball(pkgName);
stream.on('data', (data) => {
expect(data.toString()).toBe(dataTarball);
});
stream.on('open', () => {
stream.done();
stream.end();
});
stream.on('success', () => {
done();
});
stream.write(dataTarball);
}
});
test('should read a tarball', (done) => {
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
const pkgName = 'test.tar.gz';
const dataTarball = '12345';
const handler = localMemory.getPackageStorage(pkgName);
if (handler) {
const stream = handler.writeTarball(pkgName);
stream.on('open', () => {
stream.done();
stream.end();
});
stream.on('success', () => {
const readStream = handler.readTarball(pkgName);
readStream.on('data', (data) => {
expect(data.toString()).toBe(dataTarball);
done();
});
});
stream.write(dataTarball);
}
});
test('should abort read a tarball', (done) => {
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
const pkgName = 'test2.tar.gz';
const dataTarball = '12345';
const handler = localMemory.getPackageStorage(pkgName);
if (handler) {
const stream = handler.writeTarball(pkgName);
stream.on('open', () => {
stream.done();
stream.end();
});
stream.on('success', () => {
const readStream = handler.readTarball(pkgName);
readStream.on('data', () => {
readStream.abort();
});
readStream.on('error', (err) => {
expect(err).not.toBeNull();
expect(err.message).toMatch(/read has been aborted/);
done();
});
});
stream.write(dataTarball);
}
});
test('should fails read a tarball not found', (done) => {
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
const pkgName = 'test2.tar.gz';
const handler = localMemory.getPackageStorage(pkgName);
if (handler) {
const readStream = handler.readTarball('not-found');
readStream.on('error', (err) => {
expect(err).not.toBeNull();
expect(err.message).toMatch(/no such package/);
done();
});
}
});
test('should abort while write a tarball', (done) => {
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
const pkgName = 'test-abort.tar.gz';
const dataTarball = '12345';
const handler = localMemory.getPackageStorage(pkgName);
if (handler) {
const stream = handler.writeTarball(pkgName);
stream.on('error', (err) => {
expect(err).not.toBeNull();
expect(err.message).toMatch(/transmision aborted/);
done();
});
stream.on('open', () => {
stream.abort();
});
stream.write(dataTarball);
}
});
test('should delete a package', (done) => {
const localMemory: IPluginStorage<ConfigMemory> = new LocalMemory(config, defaultConfig);
const pkgName = 'test2';
const handler: IPackageStorage = localMemory.getPackageStorage(pkgName);
expect(handler).toBeDefined();
if (handler) {
handler.createPackage(pkgName, pkgExample, (err) => {
expect(err).toBeNull();
handler.deletePackage(pkgName, (err) => {
expect(err).toBeNull();
handler.readPackage(pkgName, (err) => {
expect(err).not.toBeNull();
expect(err.message).toMatch(/no such package/);
done();
});
});
});
}
});
});
});
});

View file

@ -0,0 +1,50 @@
import { Config } from '@verdaccio/types';
const config: Config = {
user_agent: 'string',
server_id: 1234,
secret: '12345',
self_path: './nowhere',
uplinks: {
npmjs: {
url: 'https://registry.npmjs.org/',
},
},
security: {
web: {
sign: {},
verify: {},
},
api: {
legacy: true,
},
},
packages: {
test: {
storage: '',
publish: [''],
proxy: [''],
access: [''],
},
},
web: {
enable: true,
title: 'string',
logo: 'string',
},
logs: [],
auth: {},
notifications: {
method: '',
packagePattern: /a/,
packagePatternFlags: '',
headers: {},
endpoint: '',
content: '',
},
checkSecretKey: () => '1234',
getMatchedPackagesSpec: jest.fn(),
hasProxyTo: () => false,
};
export default config;

View file

@ -0,0 +1,65 @@
import { Package } from '@verdaccio/types';
const pkg: Package = {
name: '@scope/test_npm',
versions: {
'1.0.1': {
name: '@scope/test_npm',
version: '1.0.1',
description: '',
main: 'index.js',
readme: 'test readme',
scripts: {
test: "echo 'Error: no test specified' && exit 1",
},
author: {
name: 'Juan Picado',
email: 'me@jotadeveloper.com',
url: 'http://jotadeveloper.com/',
},
license: 'ISC',
dependencies: {
'create-react-app': '^1.4.1',
'fast-static-site': '^1.0.2',
watchdom: '^1.0.2',
},
_id: '@scope/test_npm@1.0.1',
_npmUser: {
name: 'jpicado',
email: 'dsa@dasd.com',
},
maintainers: [
{
name: 'jpicado',
email: 'dsa@dasd.com',
},
],
dist: {
integrity:
'sha512-0ThGF2zZiOGmLoHl/n5cMwAS6swbAz7rdzDjgkyDh+C2rADzNfPIfo7KBTRHbY6uJ9akBCvWDFBuR0fgaxYnjQ==',
shasum: '1df0c3dfd289b2ac6ef00b0129cab9737eeaa62d',
tarball: 'http://localhost:4873/@scope/test_npm/-/@scope/test_npm-1.0.1.tgz',
},
},
},
'dist-tags': {
latest: '1.0.1',
},
time: {
modified: '2018-02-20T17:50:47.944Z',
created: '2018-02-20T17:50:47.944Z',
'1.0.1': '2018-02-20T17:50:47.944Z',
},
_distfiles: {},
_attachments: {
'test_npm-1.0.1.tgz': {
shasum: '1df0c3dfd289b2ac6ef00b0129cab9737eeaa62d',
version: '1.0.1',
},
},
_uplinks: {},
_rev: '5-ea87644a96a129cf',
readme: 'ERROR: No README data found!',
};
export default pkg;

View file

@ -0,0 +1,9 @@
{
"extends": "../../../tsconfig.base",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./build"
},
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.test.ts"]
}

View file

@ -0,0 +1,20 @@
{
"extends": "../../../tsconfig.reference.json",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./build"
},
"include": ["src/**/*", "types/*.d.ts"],
"exclude": ["src/**/*.test.ts"],
"references": [
{
"path": "../../core/commons-api"
},
{
"path": "../../core/streams"
},
{
"path": "../../core/types"
}
]
}

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
packages:
- packages/*
- packages/core/*
- packages/plugins/*
- website
- '!**/test/**'