mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
chore(server): eslint keeps fast-xml-parser inside place-import and the WebDAV client
This commit is contained in:
+50
-19
@@ -140,6 +140,27 @@ const MCP_DEMO_SELECTORS = [
|
||||
{ selector: "CallExpression[callee.name='demoDenied']", message: DEMO_GATE_MESSAGE },
|
||||
];
|
||||
|
||||
// XML is read in exactly two places: the GPX and KML codecs of place-import, and
|
||||
// the WebDAV client (PROPFIND answers). One parser setup per format instead of
|
||||
// one per caller is what keeps a GPX read in one module the same as in another.
|
||||
const XML_PARSER_PATH = {
|
||||
name: 'fast-xml-parser',
|
||||
message:
|
||||
'XML is parsed in src/nest/place-import (gpx.codec.ts, kml.codec.ts) and the WebDAV client only. Read GPX/KML through PlaceImportService instead of configuring another parser.',
|
||||
};
|
||||
|
||||
const DRIVER_PATH = {
|
||||
name: 'better-sqlite3',
|
||||
message:
|
||||
'The SQLite driver is an implementation detail of src/db. Go through the ORM (src/db/entities + src/db/repositories) or UnitOfWork; the handle itself is owned by src/db/database.ts.',
|
||||
};
|
||||
|
||||
const SERVICES_WALL = {
|
||||
group: ['**/services/*', '**/services/**/*'],
|
||||
message:
|
||||
'src/services/ is deleted. New backend code goes to src/nest/<domain>/ (service + controller + module, registered in app.module.ts). See src/nest/README.md.',
|
||||
};
|
||||
|
||||
export default tseslint.config(
|
||||
gitignore({ strict: false }),
|
||||
{
|
||||
@@ -319,25 +340,31 @@ export default tseslint.config(
|
||||
'src/db/reseat-booked-nights.ts',
|
||||
],
|
||||
rules: {
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: [
|
||||
{
|
||||
name: 'better-sqlite3',
|
||||
message:
|
||||
'The SQLite driver is an implementation detail of src/db. Go through the ORM (src/db/entities + src/db/repositories) or UnitOfWork; the handle itself is owned by src/db/database.ts.',
|
||||
},
|
||||
],
|
||||
patterns: [
|
||||
{
|
||||
group: ['**/services/*', '**/services/**/*'],
|
||||
message:
|
||||
'src/services/ is deleted. New backend code goes to src/nest/<domain>/ (service + controller + module, registered in app.module.ts). See src/nest/README.md.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
'no-restricted-imports': ['error', { paths: [DRIVER_PATH, XML_PARSER_PATH], patterns: [SERVICES_WALL] }],
|
||||
},
|
||||
},
|
||||
{
|
||||
// The files the block above leaves out for the driver still may not parse
|
||||
// XML: restated with XML_PARSER_PATH and the services wall only.
|
||||
files: [
|
||||
'src/db/database.ts',
|
||||
'src/db/connection.ts',
|
||||
'src/db/orm-driver.ts',
|
||||
'src/db/durability.ts',
|
||||
'src/nest/plugins/host/plugin-data.service.ts',
|
||||
'src/db/reseat-booked-nights.ts',
|
||||
],
|
||||
rules: {
|
||||
'no-restricted-imports': ['error', { paths: [XML_PARSER_PATH], patterns: [SERVICES_WALL] }],
|
||||
},
|
||||
},
|
||||
{
|
||||
// The two homes of the XML parser (see XML_PARSER_PATH). Restated without
|
||||
// it, for the same flat-config reason as above: this block's options
|
||||
// replace the previous block's for the files it matches.
|
||||
files: ['src/nest/place-import/**/*.ts', 'src/nest/doc-sync/providers/webdav.client.ts'],
|
||||
rules: {
|
||||
'no-restricted-imports': ['error', { paths: [DRIVER_PATH], patterns: [SERVICES_WALL] }],
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -372,6 +399,7 @@ export default tseslint.config(
|
||||
message:
|
||||
'Repositories are dialect-neutral: import from @mikro-orm/sql / @mikro-orm/core. The driver lives in src/db/orm-driver.ts.',
|
||||
},
|
||||
XML_PARSER_PATH,
|
||||
],
|
||||
patterns: [
|
||||
{
|
||||
@@ -433,6 +461,9 @@ export default tseslint.config(
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
// Restated for the flat-config reason above: without them the kit
|
||||
// would drop the driver and XML walls every other src file holds.
|
||||
paths: [DRIVER_PATH, XML_PARSER_PATH],
|
||||
patterns: [
|
||||
{
|
||||
// A regex rather than gitignore-style groups: the two allowed files sit
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import tsParser from '@typescript-eslint/parser';
|
||||
|
||||
import { ESLint, Linter } from 'eslint';
|
||||
import path from 'path';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
// fast-xml-parser is allowed in place-import's codecs and the WebDAV client
|
||||
// only, through no-restricted-imports in eslint.config.mjs. That rule's options
|
||||
// are replaced, not merged, by every flat-config block that restates it, so a
|
||||
// later block can silently drop the restriction (or the driver and services
|
||||
// walls beside it). This asks ESLint for the options it really applies to a
|
||||
// path and runs them over an import of each restricted module.
|
||||
|
||||
const serverRoot = path.resolve(__dirname, '../../..');
|
||||
const eslint = new ESLint({ cwd: serverRoot });
|
||||
|
||||
async function restrictedImportsFor(file: string): Promise<unknown[]> {
|
||||
const config = (await eslint.calculateConfigForFile(path.join(serverRoot, file))) as {
|
||||
rules?: Record<string, [string, ...unknown[]]>;
|
||||
};
|
||||
return config.rules?.['no-restricted-imports']?.slice(1) ?? [];
|
||||
}
|
||||
|
||||
async function flags(file: string, source: string): Promise<boolean> {
|
||||
const options = await restrictedImportsFor(file);
|
||||
const linter = new Linter({ configType: 'flat' });
|
||||
const result = linter.verify(
|
||||
source,
|
||||
[{ files: ['**/*.ts'], languageOptions: { parser: tsParser }, rules: { 'no-restricted-imports': ['error', ...(options as object[])] } }],
|
||||
'probe.ts',
|
||||
);
|
||||
return result.some((m) => m.ruleId === 'no-restricted-imports');
|
||||
}
|
||||
|
||||
const XML = "import { XMLParser } from 'fast-xml-parser';\n";
|
||||
const DRIVER = "import Database from 'better-sqlite3';\n";
|
||||
const SERVICES = "import { x } from '../services/x';\n";
|
||||
|
||||
describe('fast-xml-parser stays in place-import and the WebDAV client', () => {
|
||||
it.each([
|
||||
'src/nest/places/places.service.ts',
|
||||
'src/nest/collections/collections.service.ts',
|
||||
'src/nest/tours/tours.service.ts',
|
||||
'src/nest/maps/maps.service.ts',
|
||||
'src/db/repositories/Places.repository.ts',
|
||||
// The driver's own homes and the plugin sandbox's database, which the
|
||||
// driver wall leaves out, and the RPC kit, which restates the rule.
|
||||
'src/db/database.ts',
|
||||
'src/db/connection.ts',
|
||||
'src/db/orm-driver.ts',
|
||||
'src/db/durability.ts',
|
||||
'src/db/reseat-booked-nights.ts',
|
||||
'src/nest/plugins/host/plugin-data.service.ts',
|
||||
'src/nest-rpc/rpc-kit/registry.ts',
|
||||
])('refuses it in %s', async (file) => {
|
||||
expect(await flags(file, XML)).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
'src/nest/place-import/gpx.codec.ts',
|
||||
'src/nest/place-import/kml.codec.ts',
|
||||
'src/nest/doc-sync/providers/webdav.client.ts',
|
||||
])('allows it in %s, and keeps the driver and services walls there', async (file) => {
|
||||
expect(await flags(file, XML)).toBe(false);
|
||||
expect(await flags(file, DRIVER)).toBe(true);
|
||||
expect(await flags(file, SERVICES)).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps the services wall in the driver-exempt files and the driver wall in the RPC kit', async () => {
|
||||
expect(await flags('src/nest/plugins/host/plugin-data.service.ts', SERVICES)).toBe(true);
|
||||
expect(await flags('src/nest/plugins/host/plugin-data.service.ts', DRIVER)).toBe(false);
|
||||
expect(await flags('src/nest-rpc/rpc-kit/registry.ts', DRIVER)).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps the driver and services walls where the parser is refused', async () => {
|
||||
expect(await flags('src/nest/places/places.service.ts', DRIVER)).toBe(true);
|
||||
expect(await flags('src/nest/places/places.service.ts', SERVICES)).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user