Sort exports
Fix merge option Add utils tests
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
dist
|
||||
dist-test
|
||||
@@ -1,4 +1,11 @@
|
||||
# Version 1.4.2
|
||||
# Version 1.5.0 ⚠️
|
||||
|
||||
- **Sorts exported object keys**
|
||||
- Fixes issue where the order of the exported object keys would change between exports, causing unnecessary changes in git.
|
||||
- **merge option fixed**
|
||||
- The merge option was introduced in version 1.3.0, but it was not working as intended. This has now been fixed.
|
||||
|
||||
## Version 1.4.2
|
||||
|
||||
- Add `import-schema` and `export-schema` commands to import/export only the schema.
|
||||
|
||||
|
||||
30
package-lock.json
generated
30
package-lock.json
generated
@@ -1,15 +1,16 @@
|
||||
{
|
||||
"name": "directus-extension-schema-sync",
|
||||
"version": "1.4.2",
|
||||
"version": "1.5.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "directus-extension-schema-sync",
|
||||
"version": "1.4.2",
|
||||
"version": "1.5.0",
|
||||
"devDependencies": {
|
||||
"@directus/extensions-sdk": "10.1.11",
|
||||
"@directus/types": "^10.1.6",
|
||||
"@types/keyv": "^4.2.0",
|
||||
"@types/node": "^20.8.7",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
@@ -990,6 +991,16 @@
|
||||
"integrity": "sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/keyv": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-4.2.0.tgz",
|
||||
"integrity": "sha512-xoBtGl5R9jeKUhc8ZqeYaRDx04qqJ10yhhXYGmJ4Jr8qKpvMsDQQrNUvF/wUJ4klOtmJeJM+p2Xo3zp9uaC3tw==",
|
||||
"deprecated": "This is a stub types definition. keyv provides its own type definitions, so you do not need this installed.",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"keyv": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.4.tgz",
|
||||
@@ -3030,6 +3041,12 @@
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/json-buffer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
@@ -3054,6 +3071,15 @@
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"json-buffer": "3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/knex": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/knex/-/knex-2.4.2.tgz",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "directus-extension-schema-sync",
|
||||
"description": "Sync schema and data betwreen Directus instances",
|
||||
"icon": "sync_alt",
|
||||
"version": "1.4.2",
|
||||
"version": "1.5.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/bcc-code/directus-schema-sync.git"
|
||||
@@ -29,11 +29,14 @@
|
||||
"scripts": {
|
||||
"build": "directus-extension build",
|
||||
"dev": "directus-extension build -w --no-minify",
|
||||
"link": "directus-extension link"
|
||||
"link": "directus-extension link",
|
||||
"pre-test": "tsc -p tsconfig.test.json",
|
||||
"test": "npm run pre-test && node --test dist-test/"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@directus/extensions-sdk": "10.1.11",
|
||||
"@directus/types": "^10.1.6",
|
||||
"@types/keyv": "^4.2.0",
|
||||
"@types/node": "^20.8.7",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { ApiExtensionContext, Item, PrimaryKey, Query } from '@directus/types';
|
||||
import { readFile, writeFile } from 'fs/promises';
|
||||
import { condenseAction } from './condenseAction.js';
|
||||
import type { CollectionExporterOptions, IExporter, IGetItemsService, ItemsService, JSONString } from './types';
|
||||
import { ExportHelper, getDiff } from './utils.js';
|
||||
import { ExportHelper, getDiff, sortObject } from './utils.js';
|
||||
|
||||
const DEFAULT_COLLECTION_EXPORTER_OPTIONS: CollectionExporterOptions = {
|
||||
excludeFields: [],
|
||||
@@ -109,7 +109,7 @@ class CollectionExporter implements IExporter {
|
||||
const itemsSvc = await this._getService();
|
||||
const { query } = await this.settings();
|
||||
|
||||
const items = await itemsSvc.readByQuery(query);
|
||||
let items = await itemsSvc.readByQuery(query);
|
||||
if (!items.length) return '';
|
||||
|
||||
if (this.options.onExport) {
|
||||
@@ -118,10 +118,11 @@ class CollectionExporter implements IExporter {
|
||||
const alteredItem = await this.options.onExport(item, itemsSvc);
|
||||
if (alteredItem) alteredItems.push(alteredItem);
|
||||
}
|
||||
return JSON.stringify(alteredItems, null, 2);
|
||||
} else {
|
||||
return JSON.stringify(items, null, 2);
|
||||
}
|
||||
|
||||
items = alteredItems;
|
||||
}
|
||||
|
||||
return JSON.stringify(sortObject(items), null, 2);
|
||||
}
|
||||
|
||||
public async loadJSON(json: JSONString | null, merge = false) {
|
||||
|
||||
@@ -27,7 +27,7 @@ export class ExportManager {
|
||||
|
||||
// SECOND: Import if needed
|
||||
public async loadAll(merge = false) {
|
||||
await this._loadNextExporter(0);
|
||||
await this._loadNextExporter(0, merge);
|
||||
}
|
||||
|
||||
protected async _loadNextExporter(i = 0, merge = false) {
|
||||
|
||||
104
src/utils.test.ts
Normal file
104
src/utils.test.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import assert from "node:assert";
|
||||
import { describe, it } from "node:test";
|
||||
import { deepEqual, getDiff, sortObject } from "./utils.js";
|
||||
|
||||
describe('sortObject', () => {
|
||||
it('should sort object keys alphabetically', () => {
|
||||
const input = { c: 1, a: 2, b: 3 };
|
||||
const assertedOutput = { a: 2, b: 3, c: 1 };
|
||||
assert.deepStrictEqual(sortObject(input), assertedOutput);
|
||||
});
|
||||
|
||||
it('should sort nested object keys alphabetically', () => {
|
||||
const input = { c: 1, a: { d: 4, b: 3 }, e: 5 };
|
||||
const assertedOutput = { a: { b: 3, d: 4 }, c: 1, e: 5 };
|
||||
assert.deepStrictEqual(sortObject(input), assertedOutput);
|
||||
});
|
||||
|
||||
it('should sort array elements recursively', () => {
|
||||
const input = [{ c: 1, a: 2 }, { b: 3 }];
|
||||
const assertedOutput = [{ a: 2, c: 1 }, { b: 3 }];
|
||||
assert.deepStrictEqual(sortObject(input), assertedOutput);
|
||||
});
|
||||
|
||||
it('should return input if it is not an object', () => {
|
||||
assert.deepStrictEqual(sortObject(null as any), null);
|
||||
assert.deepStrictEqual(sortObject(42 as any), 42);
|
||||
assert.deepStrictEqual(sortObject('hello' as any), 'hello');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDiff', () => {
|
||||
it('should return the entire new object if the old object is null', () => {
|
||||
const newObj = { a: 1, b: 2 };
|
||||
const oldObj = null;
|
||||
const assertedOutput = { a: 1, b: 2 };
|
||||
assert.deepStrictEqual(getDiff(newObj, oldObj), assertedOutput);
|
||||
});
|
||||
|
||||
it('should return null if the new and old objects are equal', () => {
|
||||
const newObj = { a: 1, b: 2 };
|
||||
const oldObj = { a: 1, b: 2 };
|
||||
assert.deepStrictEqual(getDiff(newObj, oldObj), null);
|
||||
});
|
||||
|
||||
it('should return only the different properties between the new and old objects', () => {
|
||||
const newObj = { a: 1, b: 2, c: 3 };
|
||||
const oldObj = { a: 1, b: 3, d: 4 };
|
||||
const assertedOutput = { b: 2, c: 3 };
|
||||
assert.deepStrictEqual(getDiff(newObj, oldObj), assertedOutput);
|
||||
});
|
||||
|
||||
it('should handle nested objects', () => {
|
||||
const newObj = { a: 1, b: { c: 2, d: 3 } };
|
||||
const oldObj = { a: 1, b: { c: 2, d: 4 } };
|
||||
const assertedOutput = { b: { d: 3 } };
|
||||
assert.deepStrictEqual(getDiff(newObj, oldObj), assertedOutput);
|
||||
});
|
||||
|
||||
it('should handle arrays', () => {
|
||||
const newObj = { a: 1, b: [1, 2, 3] };
|
||||
const oldObj = { a: 1, b: [1, 2, 4] };
|
||||
const assertedOutput = { b: [1, 2, 3] };
|
||||
assert.deepStrictEqual(getDiff(newObj, oldObj), assertedOutput);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deepEqual', () => {
|
||||
it('should return true for equal objects', () => {
|
||||
const obj1 = { a: 1, b: { c: 2 } };
|
||||
const obj2 = { a: 1, b: { c: 2 } };
|
||||
assert.strictEqual(deepEqual(obj1, obj2), true);
|
||||
});
|
||||
|
||||
it('should return false for different objects', () => {
|
||||
const obj1 = { a: 1, b: { c: 2 } };
|
||||
const obj2 = { a: 1, b: { c: 3 } };
|
||||
assert.strictEqual(deepEqual(obj1, obj2), false);
|
||||
});
|
||||
|
||||
it('should return true for equal arrays', () => {
|
||||
const arr1 = [1, 2, { a: 3 }];
|
||||
const arr2 = [1, 2, { a: 3 }];
|
||||
assert.strictEqual(deepEqual(arr1, arr2), true);
|
||||
});
|
||||
|
||||
it('should return false for different arrays', () => {
|
||||
const arr1 = [1, 2, { a: 3 }];
|
||||
const arr2 = [1, 2, { a: 4 }];
|
||||
assert.strictEqual(deepEqual(arr1, arr2), false);
|
||||
});
|
||||
|
||||
it('should return true for equal primitives', () => {
|
||||
assert.strictEqual(deepEqual(1, 1), true);
|
||||
assert.strictEqual(deepEqual('hello', 'hello'), true);
|
||||
assert.strictEqual(deepEqual(null, null), true);
|
||||
assert.strictEqual(deepEqual(undefined, undefined), true);
|
||||
});
|
||||
|
||||
it('should return false for different primitives', () => {
|
||||
assert.strictEqual(deepEqual(1, 2), false);
|
||||
assert.strictEqual(deepEqual('hello', 'world'), false);
|
||||
assert.strictEqual(deepEqual(null, undefined), false);
|
||||
});
|
||||
});
|
||||
18
src/utils.ts
18
src/utils.ts
@@ -109,3 +109,21 @@ export function getDiff(newObj: Record<any, any>, oldObj: any) {
|
||||
});
|
||||
return isDifferent ? result : null;
|
||||
}
|
||||
|
||||
export function sortObject<T extends Record<string, any>>(obj: T): T;
|
||||
export function sortObject<T>(obj: T[]): T[];
|
||||
export function sortObject<T extends Record<string, any> | T[]>(obj: T): T {
|
||||
if (typeof obj !== 'object' || obj === null) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(sortObject) as unknown as T;
|
||||
}
|
||||
|
||||
const sortedObj: Record<string, any> = {};
|
||||
Object.keys(obj).sort().forEach(key => {
|
||||
sortedObj[key] = sortObject((obj as Record<string, any>)[key]);
|
||||
});
|
||||
return sortedObj as T;
|
||||
}
|
||||
@@ -26,4 +26,4 @@
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["./src/**/*.ts"]
|
||||
}
|
||||
}
|
||||
30
tsconfig.test.json
Normal file
30
tsconfig.test.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022", "DOM"],
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"esModuleInterop": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noUnusedParameters": true,
|
||||
"alwaysStrict": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictBindCallApply": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"resolveJsonModule": false,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"isolatedModules": true,
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist-test",
|
||||
},
|
||||
"include": ["src/utils.ts", "src/utils.test.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user