Skip to content

Commit

Permalink
feat: follow imports to source file
Browse files Browse the repository at this point in the history
  • Loading branch information
atanasster committed Mar 4, 2020
1 parent 07cb07b commit 280ab9d
Show file tree
Hide file tree
Showing 19 changed files with 418 additions and 132 deletions.
70 changes: 35 additions & 35 deletions core/instrument/src/babel/extract-exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import { CodeLocation } from '@component-controls/specification';
import { sourceLocation } from './utils';

export interface ExportType {
path: any;
node: any;
name: string;
internalName?: string;
loc: CodeLocation;
type: 'function' | 'indentifier';
/**
* in case of export { Button } from './button-named-export';
* specifies the import from statememnt
*/
from?: string;
}

export const EXPORT_ALL = '*';

export interface NamedExportTypes {
[key: string]: ExportType;
}
Expand All @@ -23,10 +27,7 @@ export const traverseExports = (results: ExportTypes) => {
const globals: NamedExportTypes = {};
const localExports: NamedExportTypes = {};

const extractArrowFunction = (
path: any,
declaration: any,
): ExportType | undefined => {
const extractArrowFunction = (declaration: any): ExportType | undefined => {
if (
declaration.init &&
declaration.init.type === 'ArrowFunctionExpression'
Expand All @@ -36,9 +37,6 @@ export const traverseExports = (results: ExportTypes) => {
loc: sourceLocation(declaration.init.body.loc),
name,
internalName: name,
path,
node: path.node,
type: 'function',
};
return exportType;
}
Expand All @@ -48,10 +46,7 @@ export const traverseExports = (results: ExportTypes) => {
ExportDefaultDeclaration: (path: any) => {
results.default = {
name: 'default',
path,
node: path.node.declaration,
loc: sourceLocation(path.node.loc),
type: 'indentifier',
};
},
VariableDeclaration: (path: any) => {
Expand All @@ -62,7 +57,7 @@ export const traverseExports = (results: ExportTypes) => {
const name = declaration.id.name;
//check if it was a named export
if (!results.named[name]) {
const namedExport = extractArrowFunction(path, declaration);
const namedExport = extractArrowFunction(declaration);
if (namedExport && namedExport.name) {
localExports[namedExport.name] = namedExport;
}
Expand All @@ -81,49 +76,59 @@ export const traverseExports = (results: ExportTypes) => {
namedExport.name = exportedName;
const global = globals[localName];
if (global) {
namedExport.path = global.path;
namedExport.node = global.node;
namedExport.loc = global.loc;
}
results.named[exportedName] = namedExport;
}
},
ExportNamedDeclaration: (path: any) => {
const { declaration } = path.node;
const { declaration, specifiers, source } = path.node;
if (declaration) {
const { declarations } = declaration;

if (Array.isArray(declarations)) {
declarations.forEach(declaration => {
const namedExport = extractArrowFunction(path, declaration);
const namedExport = extractArrowFunction(declaration);
if (namedExport) {
const name = namedExport.name || '';
const global = globals[name];
if (global) {
namedExport.internalName = global.name;
namedExport.name = global.name;
namedExport.path = global.path;
namedExport.node = global.node;
namedExport.loc = global.loc;
}
results.named[name] = namedExport;
}
});
}
} else if (specifiers) {
specifiers.forEach((specifier: any) => {
results.named[specifier.exported.name] = {
name: specifier.exported.name,
internalName: specifier.local
? specifier.local.name
: specifier.exported.name,
loc: sourceLocation(specifier.exported.loc),
from: source ? source.value : undefined,
};
});
}
},
ExportAllDeclaration: (path: any) => {
const node = path.node;
const { source } = node;
if (source) {
results.named[source.value] = {
name: EXPORT_ALL,
internalName: EXPORT_ALL,
loc: sourceLocation(source.loc),
from: source.value,
};
}
},
};
};

const cleanExportType = (exportType?: ExportType) => {
return exportType
? {
name: exportType.name,
internalName: exportType.internalName,
loc: exportType.loc,
}
: undefined;
};
export const extractExports = (
source: string,
parserOptions?: parser.ParserOptions,
Expand All @@ -134,10 +139,5 @@ export const extractExports = (
const ast = parser.parse(source, parserOptions);

traverse(ast, traverseExports(results));
return {
default: cleanExportType(results.default),
named: Object.keys(results.named).map(key =>
cleanExportType(results.named[key]),
),
};
return results;
};
16 changes: 5 additions & 11 deletions core/instrument/src/babel/extract-imports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ import traverse from '@babel/traverse';
export interface ImportType {
name: string;
importedName: 'default' | 'namespace' | string;
from: string;
}

export interface ImportTypes {
[key: string]: ImportType[];
[key: string]: ImportType;
}

export const traverseImports = (results: ImportTypes) => {
return {
ImportDeclaration: (path: any) => {
const node = path.node;
const imports: ImportType[] = node.specifiers.map((specifier: any) => {
node.specifiers.forEach((specifier: any) => {
let importedName;
switch (specifier.type) {
case 'ImportDefaultSpecifier':
Expand All @@ -29,19 +30,12 @@ export const traverseImports = (results: ImportTypes) => {
? specifier.imported.name
: specifier.local.name;
}
return {
results[specifier.local.name] = {
name: specifier.local.name,
importedName,
from: node.source.value,
};
});
if (Array.isArray(results[node.source.value])) {
results[node.source.value] = [
...results[node.source.value],
...imports,
];
} else {
results[node.source.value] = imports;
}
},
};
};
Expand Down
62 changes: 62 additions & 0 deletions core/instrument/src/babel/follow-imports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as fs from 'fs';
import * as resolve from 'resolve';
import * as parser from '@babel/parser';
import * as path from 'path';
import traverse from '@babel/traverse';
import { CodeLocation } from '@component-controls/specification';
import { ImportTypes, traverseImports } from './extract-imports';
import { traverseExports, ExportTypes } from './extract-exports';

export interface FollowImportType {
exportedAs: string;
filePath: string;
loc: CodeLocation;
}

export const followImports = (
importName: string,
filePath: string,
parserOptions?: parser.ParserOptions,
resolveOptions?: resolve.SyncOpts,
): FollowImportType | undefined => {
// follow the import name

const source = fs.readFileSync(filePath, 'utf8');
const ast = parser.parse(source, parserOptions);

const baseImportedName = importName.split('.')[0];

const exports: ExportTypes = {
named: {},
};
traverse(ast, traverseExports(exports));

const findExport =
baseImportedName === 'default' || baseImportedName === 'namespace'
? exports.default
: exports.named[baseImportedName];
if (findExport !== undefined) {
return {
filePath,
exportedAs: findExport.name,
loc: findExport.loc,
};
}
const imports: ImportTypes = {};
traverse(ast, traverseImports(imports));
const findImport = imports[baseImportedName];
if (findImport) {
const folderName = path.dirname(filePath);
const resolvedFilePath = resolve.sync(findImport.from, {
...resolveOptions,
basedir: folderName,
});
return followImports(
findImport.importedName,
resolvedFilePath,
parserOptions,
resolveOptions,
);
}
return undefined;
};
Loading

0 comments on commit 280ab9d

Please sign in to comment.