Skip to content

Commit

Permalink
feat(core): add option to export external library imports
Browse files Browse the repository at this point in the history
`getProjectData`can optionally include a property
for the used external libraries per file.

Via the `cli export` external libraries are always included.
  • Loading branch information
rainerhahnekamp authored Sep 12, 2024
1 parent e999253 commit f8ae8fb
Show file tree
Hide file tree
Showing 25 changed files with 1,231 additions and 329 deletions.
67 changes: 60 additions & 7 deletions packages/core/src/lib/api/get-project-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,19 @@ export type ProjectDataEntry = {
module: string;
tags: string[];
imports: string[];
externalLibraries?: string[];
};

/**
*
*/
export type Options = {
/**
* Adds a property `externalLibraries` to each entry
* that contains the external libraries, i.e. node_modules.
*/
includeExternalLibraries?: boolean;
};
export type ProjectData = Record<string, ProjectDataEntry>;

function calcOrGetTags(
Expand Down Expand Up @@ -57,9 +68,15 @@ function calcOrGetTags(
* }
* ```
*
* You can add additional properties via the options parameter.
*
* @param entryFileAbsolute absolute path to the entry file, e.g. /project/src/main.ts
* @param options additional options for the output
*/
export function getProjectData(entryFileAbsolute: string): ProjectData;
export function getProjectData(
entryFileAbsolute: string,
options?: Options,
): ProjectData;
/**
* Traverses through the imports of the entryFileRelative
* and returns the complete dependency graph.
Expand All @@ -84,26 +101,45 @@ export function getProjectData(entryFileAbsolute: string): ProjectData;
* }
* ```
*
* You can add additional properties via the options parameter.
*
* @param entryFileRelative relative path to the entry file, e.g. main.ts
* @param cwd absolute path to the entry file, e.g. /project/src
* @param options additional options for the output
*/
export function getProjectData(
entryFileRelative: string,
cwd: string,
options?: Options,
): ProjectData;

export function getProjectData(entryFile: string, cwd?: string): ProjectData {
export function getProjectData(
entryFile: string,
cwdOrOptions?: string | Options,
optionalOptions?: Options,
): ProjectData {
const fs = getFs();
const absoluteEntryFile =
cwd === undefined ? entryFile : fs.join(cwd, entryFile);
cwdOrOptions === undefined
? entryFile
: typeof cwdOrOptions === 'string'
? fs.join(cwdOrOptions, entryFile)
: entryFile;

const cwd = typeof cwdOrOptions === 'string' ? cwdOrOptions : undefined;
const options = optionalOptions
? optionalOptions
: typeof cwdOrOptions === 'object'
? cwdOrOptions
: {};

const projectInfo = init(toFsPath(absoluteEntryFile));

const data: ProjectData = {};
const tagsCache: Record<string, string[]> = {};

for (const { fileInfo } of traverseFileInfo(projectInfo.fileInfo)) {
data[fileInfo.path] = {
const entry: ProjectDataEntry = {
module: fileInfo.moduleInfo.directory || '.',
tags: calcOrGetTags(
fileInfo.moduleInfo.directory,
Expand All @@ -112,12 +148,24 @@ export function getProjectData(entryFile: string, cwd?: string): ProjectData {
),
imports: fileInfo.imports.map((fileInfo) => fileInfo.path),
};

if (options.includeExternalLibraries) {
entry.externalLibraries = [...fileInfo.getExternalLibraries()].sort(
(a, b) => a.localeCompare(b),
);
}

data[fileInfo.path] = entry;
}

return relativizeIfRequired(data, cwd);
return relativizeIfRequired(data, { ...options, cwd });
}

function relativizeIfRequired(data: ProjectData, cwd?: string): ProjectData {
function relativizeIfRequired(
data: ProjectData,
options: Options & { cwd?: string },
): ProjectData {
const { cwd } = options;
if (cwd === undefined) {
return data;
}
Expand All @@ -127,13 +175,18 @@ function relativizeIfRequired(data: ProjectData, cwd?: string): ProjectData {

const relativizedData: ProjectData = {};
for (const [modulePath, moduleData] of Object.entries(data)) {
relativizedData[relative(toFsPath(modulePath))] = {
const entry: ProjectDataEntry = {
module: relative(toFsPath(moduleData.module)),
tags: moduleData.tags,
imports: moduleData.imports.map((importPath) =>
relative(toFsPath(importPath)),
),
};

if (options.includeExternalLibraries) {
entry.externalLibraries = moduleData.externalLibraries;
}
relativizedData[relative(toFsPath(modulePath))] = entry;
}

return relativizedData;
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/lib/cli/export-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ export function exportData(...args: string[]): void {
const fs = getFs();
const entryFile = getEntryFromCliOrConfig(args[0], false);

const data = getProjectData(entryFile, fs.cwd());
const data = getProjectData(entryFile, fs.cwd(), { includeExternalLibraries: true });
cli.log(JSON.stringify(data, null, ' '));
}
201 changes: 201 additions & 0 deletions packages/core/src/lib/cli/tests/__snapshots__/export-data.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`export data > should also work with a sheriff.config.ts > sheriff-config 1`] = `
"{
"src/main.ts": {
"module": ".",
"tags": [
"root"
],
"imports": [
"src/holidays/index.ts",
"src/customers/index.ts"
],
"externalLibraries": []
},
"src/holidays/index.ts": {
"module": "src/holidays",
"tags": [
"scope:holidays"
],
"imports": [],
"externalLibraries": []
},
"src/customers/index.ts": {
"module": "src/customers",
"tags": [
"scope:customers"
],
"imports": [],
"externalLibraries": []
}
}"
`;

exports[`export data > should avoid circular dependencies > circular-dependencies 1`] = `
"{
"src/main.ts": {
"module": ".",
"tags": [
"root"
],
"imports": [
"src/app1.service.ts",
"src/app2.service.ts"
],
"externalLibraries": []
},
"src/app1.service.ts": {
"module": ".",
"tags": [
"root"
],
"imports": [
"src/app2.service.ts"
],
"externalLibraries": []
},
"src/app2.service.ts": {
"module": ".",
"tags": [
"root"
],
"imports": [
"src/app1.service.ts"
],
"externalLibraries": []
}
}"
`;

exports[`export data > should skip not reachable files > not-reachable-files 1`] = `
"{
"src/main.ts": {
"module": ".",
"tags": [
"root"
],
"imports": [
"src/app1.service.ts"
],
"externalLibraries": []
},
"src/app1.service.ts": {
"module": ".",
"tags": [
"root"
],
"imports": [],
"externalLibraries": []
}
}"
`;

exports[`export data > should test a simple application > simple-application 1`] = `
"{
"src/main.ts": {
"module": ".",
"tags": [
"root"
],
"imports": [
"src/holidays/feature/index.ts"
],
"externalLibraries": []
},
"src/holidays/feature/index.ts": {
"module": "src/holidays/feature",
"tags": [
"domain:holidays",
"type:feature"
],
"imports": [
"src/holidays/feature/holidays-container.component.ts"
],
"externalLibraries": []
},
"src/holidays/feature/holidays-container.component.ts": {
"module": "src/holidays/feature",
"tags": [
"domain:holidays",
"type:feature"
],
"imports": [
"src/holidays/data/index.ts",
"src/holidays/ui/index.ts",
"src/holidays/model/index.ts"
],
"externalLibraries": [
"@angular/common",
"@angular/core",
"lodash"
]
},
"src/holidays/data/index.ts": {
"module": "src/holidays/data",
"tags": [
"domain:holidays",
"type:data"
],
"imports": [
"src/holidays/data/holidays-store.ts"
],
"externalLibraries": []
},
"src/holidays/data/holidays-store.ts": {
"module": "src/holidays/data",
"tags": [
"domain:holidays",
"type:data"
],
"imports": [
"src/holidays/model/index.ts"
],
"externalLibraries": [
"@ngrx/signals"
]
},
"src/holidays/model/index.ts": {
"module": "src/holidays/model",
"tags": [
"domain:holidays",
"type:model"
],
"imports": [
"src/holidays/model/holiday.ts"
],
"externalLibraries": []
},
"src/holidays/model/holiday.ts": {
"module": "src/holidays/model",
"tags": [
"domain:holidays",
"type:model"
],
"imports": [],
"externalLibraries": []
},
"src/holidays/ui/index.ts": {
"module": "src/holidays/ui",
"tags": [
"domain:holidays",
"type:ui"
],
"imports": [
"src/holidays/ui/holidays.component.ts"
],
"externalLibraries": []
},
"src/holidays/ui/holidays.component.ts": {
"module": "src/holidays/ui",
"tags": [
"domain:holidays",
"type:ui"
],
"imports": [
"src/holidays/model/index.ts"
],
"externalLibraries": []
}
}"
`;
Loading

0 comments on commit f8ae8fb

Please sign in to comment.