From 061960f789e4f9aa1795399dbb36a2540f38e232 Mon Sep 17 00:00:00 2001 From: Jerko Steiner Date: Mon, 16 Sep 2019 22:57:46 +0700 Subject: [PATCH] Add script for listing dependencies of projects --- packages/scripts/src/getFolders.ts | 11 ++ packages/scripts/src/scripts/imports.ts | 118 ++++++++++++++++++ packages/scripts/src/scripts/index.ts | 1 + packages/scripts/src/scripts/syncEsmConfig.ts | 7 +- 4 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 packages/scripts/src/getFolders.ts create mode 100644 packages/scripts/src/scripts/imports.ts diff --git a/packages/scripts/src/getFolders.ts b/packages/scripts/src/getFolders.ts new file mode 100644 index 0000000..54a24c9 --- /dev/null +++ b/packages/scripts/src/getFolders.ts @@ -0,0 +1,11 @@ +import { readdirSync, lstatSync } from "fs" +import { join } from "path" + +export function getFolders(dir: string): string[] { + return readdirSync(dir) + .map(file => join(dir, file)) + .filter(dir => { + const stat = lstatSync(dir) + return stat.isDirectory() + }) +} diff --git a/packages/scripts/src/scripts/imports.ts b/packages/scripts/src/scripts/imports.ts new file mode 100644 index 0000000..4544ee0 --- /dev/null +++ b/packages/scripts/src/scripts/imports.ts @@ -0,0 +1,118 @@ +import * as ts from 'typescript' +import {argparse, arg} from '@rondo.dev/argparse' +import {info, error} from '../log' +import { getFolders } from '../getFolders' + +export function imports(...argv: string[]): string[] { + const args = argparse({ + packages: arg('string', {default: 'packages/', positional: true}), + root: arg('string', {default: 'package.json'}), + debug: arg('boolean'), + help: arg('boolean', {alias: 'h'}), + output: arg('string', {alias: 'o', default: '-'}), + }).parse(argv) + + function debug(m: string, ...meta: Array) { + if (args.debug) { + info(m, ...meta) + } + } + + /** Generate interfaces for all exported classes in a set of .ts files */ + function collectImports( + projectDir: string, + tsconfigFileName: string, + ): string[] { + // Build a program using the set of root file names in fileNames + // const program = ts.createProgram(fileNames, options) + const configPath = ts.findConfigFile( + projectDir, + ts.sys.fileExists, + tsconfigFileName, + ) + if (!configPath) { + throw new Error('No tsconfig.json found') + } + + const parseConfigHost: ts.ParseConfigHost = { + fileExists: ts.sys.fileExists, + readFile: ts.sys.readFile, + readDirectory: ts.sys.readDirectory, + useCaseSensitiveFileNames: true, + } + const configFile = ts.readConfigFile(configPath, ts.sys.readFile); + const parsedCommandLine = ts.parseJsonConfigFileContent( + configFile.config, + parseConfigHost, + projectDir, + ); + + const program = ts.createProgram( + parsedCommandLine.fileNames, + parsedCommandLine.options, + ) + + const modules = new Set() + + // Get the checker, we will use it to find more about classes + // const checker = program.getTypeChecker() + + function isInstalledModule(moduleName: string) { + return !moduleName.startsWith('.') + } + + /** + * Visit nodes finding exported classes + */ + function visit(sourceFile: ts.SourceFile, node: ts.Node) { + if (ts.isImportDeclaration(node)) { + const text = node.moduleSpecifier.getText(sourceFile) + const name = text.substring(1, text.length - 1) + if (isInstalledModule(name)) { + let resolved: string + try { + resolved = require.resolve(name) + } catch (err) { + error('Warning require.resolve: %s', err.message) + return + } + + if (resolved === name) { + // Names of modules provided by NodeJS like "fs" will be the same + return + } + + if (!modules.has(name)) { + debug(name) + modules.add(name) + } + + } + + ts.forEachChild(node, visit.bind(null, sourceFile)) + } + } + + for (const sourceFile of program.getSourceFiles()) { + if (!sourceFile.isDeclarationFile) { + ts.forEachChild(sourceFile, visit.bind(null, sourceFile)) + } + } + + return Array.from(modules) + } + + getFolders(args.packages) + .forEach(pkgDir => { + error('Entering: %s', pkgDir) + const modules = collectImports(pkgDir, 'tsconfig.json') + }) + + if (args.output === '-') { + // info(value) + } else { + // fs.writeFileSync(args.output, value) + } + + return [] +} diff --git a/packages/scripts/src/scripts/index.ts b/packages/scripts/src/scripts/index.ts index 0fdb7cb..65df646 100644 --- a/packages/scripts/src/scripts/index.ts +++ b/packages/scripts/src/scripts/index.ts @@ -2,6 +2,7 @@ export * from './add' export * from './build' export * from './clean' export * from './exportDir' +export * from './imports' export * from './intergen' export * from './syncEsmConfig' export * from './update' diff --git a/packages/scripts/src/scripts/syncEsmConfig.ts b/packages/scripts/src/scripts/syncEsmConfig.ts index c83990f..eb2c5e1 100644 --- a/packages/scripts/src/scripts/syncEsmConfig.ts +++ b/packages/scripts/src/scripts/syncEsmConfig.ts @@ -2,6 +2,7 @@ import * as fs from 'fs' import * as path from 'path' import {argparse, arg} from '@rondo.dev/argparse' import {info} from '../log' +import { getFolders } from '../getFolders' const TSCONFIG_FILENAME = 'tsconfig.json' const TSCONFIG_ESM_FILENAME = 'tsconfig.esm.json' @@ -19,11 +20,7 @@ export async function syncEsmConfig(...argv: string[]) { const pkgDir = args.packages - fs.readdirSync(pkgDir) - .filter(file => { - const stat = fs.lstatSync(path.join(pkgDir, file)) - return stat.isDirectory() - }) + getFolders(pkgDir) .map(file => path.join(pkgDir, file, TSCONFIG_FILENAME)) .filter(file => fs.existsSync(file)) .forEach(file => {