Skip to content

Commit

Permalink
feat: match by extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
bodinsamuel committed Oct 27, 2023
1 parent e2978a9 commit 9649581
Show file tree
Hide file tree
Showing 28 changed files with 206 additions and 46 deletions.
1 change: 1 addition & 0 deletions src/analyser/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ exports[`analyser > should run correctly 1`] = `
"vite matched: /^vite$/",
"matched file: .vercel",
"matched file: package.json",
"matched extension: .scss",
],
"tech": null,
"techs": [
Expand Down
14 changes: 14 additions & 0 deletions src/common/rules/matchFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,17 @@ export function matchFilesRegex(

return false;
}

export function matchExtensions(
key: AllowedKeys,
list: string[],
extensions: Set<string>
): false | [Rule, string] {
for (const ext of list) {
if (extensions.has(ext)) {
return [listIndexed[key], ext];
}
}

return false;
}
19 changes: 17 additions & 2 deletions src/loader.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { matchFiles, matchFilesRegex } from './common/rules/matchFiles.js';
import {
matchExtensions,
matchFiles,
matchFilesRegex,
} from './common/rules/matchFiles.js';
import { registeredRules } from './register.js';
import type {
ComponentMatcher,
ExtensionMatcher,
Rule,
RuleDependency,
RuleWithFile,
Expand All @@ -11,7 +16,7 @@ import type {
import type { AllowedKeys } from './types/techs.js';

export const rulesTechs: TechMatcher[] = [];

export const rulesExtensions: ExtensionMatcher[] = [];
export const rulesComponents: ComponentMatcher[] = [];

export const dependencies: Record<
Expand All @@ -32,6 +37,7 @@ export const dependencies: Record<

export const rawList: Array<
| ({ ref: RuleDependency } & { type: 'dependency' })
| ({ ref: RuleWithFile } & { type: 'ext' })
| ({ ref: RuleWithFile } & { type: 'file' })
> = [];

Expand Down Expand Up @@ -86,6 +92,15 @@ export function loadOne(rule: Rule) {
rulesTechs.push(matcher);
}

if (typeof rule.extensions !== 'undefined') {
const matcher: ExtensionMatcher = (list) => {
return matchExtensions(rule.tech, rule.extensions!, list);
};

rawList.push({ type: 'ext', ref: rule });
rulesExtensions.push(matcher);
}

if (rule.detect) {
rulesComponents.push(
...(Array.isArray(rule.detect) ? rule.detect : [rule.detect])
Expand Down
35 changes: 35 additions & 0 deletions src/matchAllFiles.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, expect, it } from 'vitest';

import { matchAllFiles } from './matchAllFiles.js';

import './autoload.js';

describe('matchAllFiles', () => {
it('should find nothing', () => {
const res = matchAllFiles([], '/');
expect(res).toStrictEqual(new Map());
});

it('should find extension based matching: typescript', () => {
const res = matchAllFiles(
[{ fp: '/index.tsx', name: 'index.tsx', type: 'file' }],
'/'
);

const exp = new Map();
exp.set('typescript', ['matched extension: .tsx']);
exp.set('react', ['matched extension: .tsx']);
expect(res).toStrictEqual(exp);
});

it('should find file based matching: nodejs', () => {
const res = matchAllFiles(
[{ fp: '/package.json', name: 'package.json', type: 'file' }],
'/'
);

const exp = new Map();
exp.set('nodejs', ['matched file: package.json']);
expect(res).toStrictEqual(exp);
});
});
23 changes: 22 additions & 1 deletion src/matchAllFiles.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { rulesTechs } from './loader.js';
import path from 'node:path';

import { rulesExtensions, rulesTechs } from './loader.js';

import type { AllowedKeys, ProviderFile } from './index.js';

Expand All @@ -7,6 +9,8 @@ export function matchAllFiles(
basePath: string
): Map<AllowedKeys, string[]> {
const matched = new Map<AllowedKeys, string[]>();

// Match files
for (const rule of rulesTechs) {
const res = rule(files);
if (!res) {
Expand All @@ -16,5 +20,22 @@ export function matchAllFiles(
matched.set(res[0].tech, [`matched file: ${res[1].replace(basePath, '')}`]);
}

// Match extensions
const exts = new Set<string>();
for (const file of files) {
exts.add(path.extname(file.name));
}
for (const rule of rulesExtensions) {
const res = rule(exts);
if (!res) {
continue;
}
if (matched.has(res[0].tech)) {
continue;
}

matched.set(res[0].tech, [`matched extension: ${res[1]}`]);
}

return matched;
}
5 changes: 4 additions & 1 deletion src/matchDependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ export function matchDependencies(
for (const ref of dependencies[type]) {
if (ref.match.test(dep)) {
if (matched.has(ref.tech)) {
matched.set(ref.tech, [...matched.get(ref.tech)!, `matched: ${ref}`]);
matched.set(ref.tech, [
...matched.get(ref.tech)!,
`matched: ${ref.tech}`,
]);
} else {
matched.set(ref.tech, [`${ref.tech} matched: ${ref.match}`]);
}
Expand Down
8 changes: 4 additions & 4 deletions src/payload/__snapshots__/helpers.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Payload {
"path": Set {
"/",
},
"reason": [],
"reason": Set {},
"tech": null,
"techs": Set {},
},
Expand Down Expand Up @@ -43,7 +43,7 @@ Payload {
"path": Set {
"/",
},
"reason": [],
"reason": Set {},
"tech": null,
"techs": Set {},
},
Expand All @@ -58,7 +58,7 @@ Payload {
"path": Set {
"/",
},
"reason": [],
"reason": Set {},
"tech": null,
"techs": Set {},
},
Expand All @@ -73,7 +73,7 @@ Payload {
"path": Set {
"",
},
"reason": [],
"reason": Set {},
"tech": null,
"techs": Set {},
}
Expand Down
17 changes: 17 additions & 0 deletions src/payload/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { describe, expect, it, vi } from 'vitest';

import { matchAllFiles } from '../matchAllFiles.js';

import { Payload } from './index.js';

import '../autoload.js';
Expand Down Expand Up @@ -51,4 +53,19 @@ describe('Payload', () => {
expect(root.childs[0].path).toStrictEqual(new Set(['foo', 'bar.xml']));
expect(root.toJson().childs[0].path).toStrictEqual(['foo', 'bar.xml']);
});

it('should dedup matched tech', () => {
const root = new Payload({ name: 'root', folderPath: '/' });
const res = matchAllFiles(
[
{ name: 'index.ts', fp: 'index.ts', type: 'file' },
{ name: 'script.ts', fp: 'scripts.ts', type: 'file' },
],
'/'
);
root.addTechs(res);

expect(root.techs).toStrictEqual(new Set(['typescript']));
expect(root.reason).toStrictEqual(new Set(['matched extension: .ts']));
});
});
18 changes: 8 additions & 10 deletions src/payload/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class Payload implements Analyser {
public dependencies: Analyser['dependencies'];
public edges: Analyser['edges'];
public parent?: Payload | null;
public reason: string[];
public reason: Set<string>;

constructor({
id,
Expand Down Expand Up @@ -56,10 +56,8 @@ export class Payload implements Analyser {
this.languages = {};
this.dependencies = dependencies || [];
this.reason = Array.isArray(reason)
? reason
: typeof reason === 'string'
? [reason]
: [];
? new Set(reason)
: new Set(typeof reason === 'string' ? [reason] : []);

this.parent = parent;
this.edges = [];
Expand Down Expand Up @@ -93,9 +91,7 @@ export class Payload implements Analyser {

// Detect Tech
const matched = matchAllFiles(files, provider.basePath);
for (const match of matched.entries()) {
ctx.addTech(match[0], match[1]);
}
ctx.addTechs(matched);

// Recursively dive in folders
for (const file of files) {
Expand Down Expand Up @@ -181,7 +177,9 @@ export class Payload implements Analyser {
*/
addTech(tech: AllowedKeys, reason: string[]) {
this.techs.add(tech);
this.reason.push(...reason);
for (const r of reason) {
this.reason.add(r);
}

findImplicitComponent(this, tech, reason);
findHosting(this, tech);
Expand Down Expand Up @@ -313,7 +311,7 @@ export class Payload implements Analyser {
techs: [...this.techs].sort(),
languages: this.languages,
dependencies: this.dependencies,
reason: this.reason,
reason: Array.from(this.reason.values()),
};
}
}
55 changes: 55 additions & 0 deletions src/rules/files.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { describe, expect, it } from 'vitest';

import { registeredRules } from '../register.js';

import '../autoload.js';

describe('all', () => {
it('should not list multiple times the same files', () => {
const files = new Set<string>();
for (const rule of registeredRules) {
if (!rule.files) {
continue;
}
if (rule.files instanceof RegExp) {
const str = rule.files.toString();
if (files.has(str)) {
expect(rule.files).toBe(false);
continue;
}

files.add(str);
continue;
}

for (const file of rule.files) {
if (files.has(file)) {
expect(file).toBe(false);
continue;
}

files.add(file);
}
}
});

// eslint-disable-next-line vitest/no-commented-out-tests
// it('should not list multiple times the same extensions', () => {
// const exts = new Set<string>();
// for (const rule of registeredRules) {
// if (!rule.extensions) {
// continue;
// }

// for (const ext of rule.extensions) {
// if (exts.has(ext)) {
// console.error(ext);
// // expect(ext).toBe(false);
// continue;
// }

// exts.add(ext);
// }
// }
// });
});
28 changes: 0 additions & 28 deletions src/rules/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,6 @@ describe('all', () => {
expect(Array.from(registeredTech.values()).sort()).toMatchSnapshot();
});

it('should not list multiple times the same files', () => {
const files = new Set<string>();
for (const rule of registeredRules) {
if (!rule.files) {
continue;
}
if (rule.files instanceof RegExp) {
const str = rule.files.toString();
if (files.has(str)) {
expect(rule.files).toBe(false);
continue;
}

files.add(str);
continue;
}

for (const file of rule.files) {
if (files.has(file)) {
expect(file).toBe(false);
continue;
}

files.add(file);
}
}
});

it('should not list multiple times the same name', () => {
const names = new Set<string>();
for (const rule of registeredRules) {
Expand Down
1 change: 1 addition & 0 deletions src/rules/js/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ register({
tech: 'react',
name: 'React',
type: 'language',
extensions: ['.tsx', '.jsx'],
dependencies: [{ type: 'npm', name: 'react' }],
});
1 change: 1 addition & 0 deletions src/rules/js/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ register({
type: 'language',
files: /tsconfig(.[a-zA-Z0-9_-]+)?.json/,
example: 'tsconfig.json',
extensions: ['.ts', '.tsx'],
dependencies: [{ type: 'npm', name: 'typescript' }],
});
1 change: 1 addition & 0 deletions src/rules/language/bash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ register({
tech: 'bash',
name: 'Bash',
type: 'language',
extensions: ['.sh'],
});
1 change: 1 addition & 0 deletions src/rules/language/c.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ register({
tech: 'c',
name: 'C',
type: 'language',
extensions: ['.c', '.h', '.idc'],
});
Loading

0 comments on commit 9649581

Please sign in to comment.