Creates a module graph based on a given entrypoint.
npm install @thepassle/module-graphCreates a module graph based on a given entrypoint. Supports ESM, monorepos, import attributes, typescript (via a plugin) and is extensible via plugins. Builds on top of es-module-lexer for scanning a module's imports, and oxc-resolver for module resolution.
```
npm i @thepassle/module-graph
`js
import { createModuleGraph } from '@thepassle/module-graph';
const moduleGraph = await createModuleGraph('./index.js');
/**
* Multiple entrypoints
*/
const moduleGraph = await createModuleGraph(['./foo.js', './bar.js']);
/**
* Configuration options
* Supports all oxc-resolver's NapiResolveOptions options.`
* https://github.com/oxc-project/oxc-resolver?tab=readme-ov-file#oxc-resolver
*/
const moduleGraph = await createModuleGraph('./index.js', {
basePath: process.cwd(),
exportConditions: ['browser', 'import'],
/* Handle external modules /
external: {
/* Ignore all external modules imported via a bare module specifier /
ignore: true,
/* Only include external modules from these packages /
include: ['bar'],
/* Exclude bare module specifiers /
exclude: ['foo', '@foo/bar'],
},
/* Picomatch glob pattern or callback /
exclude: [
'**/ignore.js',
'*/foo/.js',
(importee) => importee.includes('foo')
],
/* Ignores dynamic imports /
ignoreDynamicImport: true,
plugins: [myPlugin]
});
createModuleGraph analyzes only ESM-style imports, not require. However, if a CommonJS file is found and uses a dynamic import, it will include the dynamic import in the graph and any other imports that leads to.
If you want to analyze typescript source code, you can use the typescript plugin:
`js
import { createModuleGraph } from '@thepassle/module-graph';
import { typescript } from '@thepassle/module-graph/plugins/typescript.js';
const moduleGraph = await createModuleGraph('./index.ts', {
plugins: [typescript()]
});
`
The default is set to ESM, which means it expects .js file extensions in your code. However, you can also provide your tsconfig.json options to the typescript plugin, to resolve extensionless typescript imports, e.g.: import { Foo } from './foo';:
`js
import { createModuleGraph } from '@thepassle/module-graph';
import { typescript } from '@thepassle/module-graph/plugins/typescript.js';
const moduleGraph = await createModuleGraph('./index.ts', {
plugins: [typescript({
compilerOptions: {
moduleResolution: "node",
}
})]
});
`
`bashList all modules in the graph
npx @thepassle/module-graph index.js
npx @thepassle/module-graph foo.js,bar.js
All CLI commands also allow the
--ts option if your source code is in Typescript, and this can be combined with the --node flag if you're using extensionless imports in Typescript. E.g.:`bash
npx @thepassle/module-graph find entrypoint.ts module-to-find.ts --ts --node
`ModuleGraph$3
`js
const moduleGraph = await createModuleGraph('./index.js');const foo = moduleGraph.get('foo.js');
/* Or use picomatch pattern /
const bar = moduleGraph.get('**/bar.js');
/**
* Or:
*/
const foo = moduleGraph.get((p) => p.endsWith('foo.js'));
`$3
`js
const moduleGraph = await createModuleGraph('./index.js');const uniqueModules = moduleGraph.getUniqueModules();
`$3
`js
const moduleGraph = await createModuleGraph('./index.js');const chains = moduleGraph.findImportChains('baz.js');
/**
* Or:
*/
const chains = moduleGraph.findImportChains((p) => p.endsWith('baz.js'));
chains.forEach((c) => console.log(c.join(" -> ")));
// index.js -> bar.js -> baz.js
`Plugins
You can also extend the default behavior by providing plugins. There are several default, opt-in plugins available:
- Typescript analyze TS source code. Takes a
compilerOptions object.
- Imports outputs additional analysis of every modules imports on the Module object
- Exports outputs additional analysis of every modules exports on the Module object
- Barrel-file analyzes every module to see if it's a barrel file
- Unused-exports finds unused exports in your module graph`js
import { typescript } from '@thepassle/module-graph/plugins/typescript.js';
import { imports } from '@thepassle/module-graph/plugins/imports.js';
import { exports } from '@thepassle/module-graph/plugins/exports.js';
import { barrelFile } from '@thepassle/module-graph/plugins/barrel-file.js';
import { unusedExports } from '@thepassle/module-graph/plugins/unused-exports.js';const moduleGraph = await createModuleGraph('./index.js', {
plugins: [
typescript(),
imports,
exports,
unusedExports,
barrelFile({
amountOfExportsToConsiderModuleAsBarrel: 3
})
]
});
const module = moduleGraph.get('index.js');
module.imports; // Array of
Import
module.exports; // Array of Export
module.isBarrelFile; // true
module.unusedExports; // Array of Export
`See the documentation for more information on the
Import and Export objects.Creating plugins
$3
All plugin hooks can be async.
####
start> Runs once
Use for initializing logic of the plugin
`js
const plugin = {
name: 'my-plugin',
start: ({entrypoints, basePath, exportConditions}) => {
console.log('Plugin start');
}
}const moduleGraph = await createModuleGraph('./index.js', {
plugins: [plugin]
});
`####
transformSource> Runs for every file
Can be used to extract JS from non-js files, like Vue or Svelte files.
`js
const plugin = {
name: 'my-plugin',
transformSource: ({ source }) => {
const match = source.match(/