diff --git a/src/compiler/build/build-utils.ts b/src/compiler/build/build-utils.ts
index c26800ab244..bc04fc03d16 100644
--- a/src/compiler/build/build-utils.ts
+++ b/src/compiler/build/build-utils.ts
@@ -22,6 +22,8 @@ export function getBuildContext(config: Config, compilerCtx: CompilerCtx, watche
const buildCtx: BuildCtx = {
requiresFullBuild: requiresFullBuild,
buildId: compilerCtx.activeBuildId,
+ componentRefs: [],
+ moduleGraph: [],
diagnostics: [],
entryPoints: [],
entryModules: [],
diff --git a/src/compiler/collections/upgrade-collection.ts b/src/compiler/collections/upgrade-collection.ts
index 5460b87e3d4..b20399564a8 100644
--- a/src/compiler/collections/upgrade-collection.ts
+++ b/src/compiler/collections/upgrade-collection.ts
@@ -55,7 +55,7 @@ function createDoUpgrade(config: Config, compilerCtx: CompilerCtx, buildCtx: Bui
case CompilerUpgrade.Add_Component_Dependencies:
config.logger.debug(`Add_Component_Dependencies, ${manifest.collectionName}, compiled by v${manifest.compiler.version}`);
return (transformContext: ts.TransformationContext) => {
- return componentDependencies(compilerCtx.moduleFiles)(transformContext);
+ return componentDependencies(compilerCtx, buildCtx)(transformContext);
};
}
return () => (tsSourceFile: ts.SourceFile) => (tsSourceFile);
diff --git a/src/compiler/entries/component-dependencies.ts b/src/compiler/entries/component-dependencies.ts
new file mode 100644
index 00000000000..2d2ce533657
--- /dev/null
+++ b/src/compiler/entries/component-dependencies.ts
@@ -0,0 +1,43 @@
+import { ComponentMeta, ComponentReference, ModuleFiles, ModuleGraph } from '../../declarations';
+
+
+export function calcComponentDependencies(moduleFiles: ModuleFiles, moduleGraphs: ModuleGraph[], componentRefs: ComponentReference[]) {
+ Object.keys(moduleFiles).forEach(filePath => {
+ const moduleFile = moduleFiles[filePath];
+ if (moduleFile.cmpMeta) {
+ getComponentDependencies(moduleGraphs, componentRefs, filePath, moduleFile.cmpMeta);
+ }
+ });
+}
+
+
+function getComponentDependencies(moduleGraphs: ModuleGraph[], componentRefs: ComponentReference[], filePath: string, cmpMeta: ComponentMeta) {
+ cmpMeta.dependencies = componentRefs.filter(cr => cr.filePath === filePath).map(cr => cr.tag);
+
+ const moduleGraph = moduleGraphs.find(mg => mg.filePath === filePath);
+ if (moduleGraph) {
+ getComponentDepsFromImports(moduleGraphs, componentRefs, moduleGraph, cmpMeta);
+ }
+
+ cmpMeta.dependencies.sort();
+}
+
+
+function getComponentDepsFromImports(moduleGraphs: ModuleGraph[], componentRefs: ComponentReference[], moduleGraph: ModuleGraph, cmpMeta: ComponentMeta) {
+ moduleGraph.importPaths.forEach(importPath => {
+ const subModuleGraph = moduleGraphs.find(mg => {
+ return (mg.filePath === importPath) ||
+ (mg.filePath === importPath + '.ts') ||
+ (mg.filePath === importPath + '.tsx') ||
+ (mg.filePath === importPath + '.js');
+ });
+
+ if (subModuleGraph) {
+ const tags = componentRefs.filter(cr => cr.filePath === subModuleGraph.filePath).map(cr => cr.tag);
+
+ cmpMeta.dependencies.push(...tags);
+
+ getComponentDepsFromImports(moduleGraphs, componentRefs, subModuleGraph, cmpMeta);
+ }
+ });
+}
diff --git a/src/compiler/transpile/test/transpile.spec.ts b/src/compiler/transpile/test/transpile.spec.ts
index d3c0b23a6b7..093d296583d 100644
--- a/src/compiler/transpile/test/transpile.spec.ts
+++ b/src/compiler/transpile/test/transpile.spec.ts
@@ -187,6 +187,62 @@ describe('transpile', () => {
expect(content).toContain(`"str": { "type": String, "attr": "str" }`);
});
+ it('get component dependencies from imports', async () => {
+ c.config.bundles = [ { components: ['cmp-a'] } ];
+ await c.fs.writeFiles({
+ '/src/new-dir/cmp-b.tsx': `@Component({ tag: 'cmp-b' }) export class CmpB {}`,
+ '/src/new-dir/cmp-c.tsx': `@Component({ tag: 'cmp-c' }) export class CmpC {}`,
+ '/src/new-dir/cmp-d.tsx': `@Component({ tag: 'cmp-d' }) export class CmpD {}`,
+ '/src/new-dir/cmp-e.tsx': `@Component({ tag: 'cmp-e' }) export class CmpE {}`,
+ '/src/util-1.tsx': `
+ import { getImportedCmpC } from './util-2';
+ export function getCmpB() {
+ const el = document.createElement("cmp-b");
+ return el;
+ }
+ export function getCmpC() {
+ return getImportedCmpC();
+ }
+ `,
+ '/src/util-2.tsx': `
+ import { getJsxCmpD } from './util-3';
+ export function getImportedCmpC() {
+ return {
+ cmpC: document.createElement("cmp-c"),
+ cmpD: getJsxCmpD()
+ };
+ }
+ `,
+ '/src/util-3.tsx': `
+ export function getJsxCmpD() {
+ return ;
+ }
+ export function getJsxCmpE() {
+ return document.createElement('cmp-e');
+ }
+ `
+ }, { clearFileCache: true });
+
+ await c.fs.writeFile('/src/cmp-a.tsx', `
+ import { getCmpB, getCmpC } from './util-1';
+
+ @Component({ tag: 'cmp-a' }) export class CmpA {
+ componentWillLoad() {
+ getCmpB();
+ }
+ componentDidLoad() {
+ getCmpC();
+ }
+ }
+ `, { clearFileCache: true });
+ await c.fs.commit();
+
+ const r = await c.build();
+ expect(r.diagnostics).toEqual([]);
+
+ expect(r.components[0].dependencies).toEqual(['cmp-b', 'cmp-c', 'cmp-d', 'cmp-e']);
+ });
+
it('get CallExpression component dependencies', async () => {
c.config.bundles = [ { components: ['cmp-a'] } ];
await c.fs.writeFiles({
diff --git a/src/compiler/transpile/transformers/component-dependencies.ts b/src/compiler/transpile/transformers/component-dependencies.ts
index 30afe58db95..8699a6d708f 100644
--- a/src/compiler/transpile/transformers/component-dependencies.ts
+++ b/src/compiler/transpile/transformers/component-dependencies.ts
@@ -1,83 +1,92 @@
-import { ComponentMeta, ModuleFiles } from '../../../declarations';
+import { BuildCtx, CompilerCtx } from '../../../declarations';
import { MEMBER_TYPE } from '../../../util/constants';
+import { normalizePath } from '../../util';
import * as ts from 'typescript';
-export function componentDependencies(allModuleFiles: ModuleFiles): ts.TransformerFactory {
+export function componentDependencies(compilerCtx: CompilerCtx, buildCtx: BuildCtx): ts.TransformerFactory {
- const allComponentTags = Object.keys(allModuleFiles)
- .map(filePath => allModuleFiles[filePath].cmpMeta)
+ const allComponentTags = Object.keys(compilerCtx.moduleFiles)
+ .map(filePath => compilerCtx.moduleFiles[filePath].cmpMeta)
.filter(cmpMeta => cmpMeta && cmpMeta.tagNameMeta)
.map(cmpMeta => cmpMeta.tagNameMeta);
return (transformContext) => {
- function visit(node: ts.Node, cmpMeta: ComponentMeta): ts.VisitResult {
+ function visit(node: ts.Node, filePath: string): ts.VisitResult {
if (node.kind === ts.SyntaxKind.CallExpression) {
- callExpression(allComponentTags, cmpMeta, node as ts.CallExpression);
+ callExpression(buildCtx, allComponentTags, filePath, node as ts.CallExpression);
} else if (node.kind === ts.SyntaxKind.StringLiteral) {
- stringLiteral(allComponentTags, cmpMeta, node as ts.StringLiteral);
+ stringLiteral(buildCtx, allComponentTags, filePath, node as ts.StringLiteral);
}
return ts.visitEachChild(node, (node) => {
- return visit(node, cmpMeta);
+ return visit(node, filePath);
}, transformContext);
}
return (tsSourceFile) => {
- const moduleFile = allModuleFiles[tsSourceFile.fileName];
+ addPropConnects(compilerCtx, buildCtx, tsSourceFile.fileName);
- if (moduleFile && moduleFile.cmpMeta) {
- moduleFile.cmpMeta.dependencies = moduleFile.cmpMeta.dependencies || [];
+ return visit(tsSourceFile, tsSourceFile.fileName) as ts.SourceFile;
+ };
+ };
+}
- if (moduleFile.cmpMeta.membersMeta) {
- Object.keys(moduleFile.cmpMeta.membersMeta).forEach(memberName => {
- const memberMeta = moduleFile.cmpMeta.membersMeta[memberName];
- if (memberMeta.memberType === MEMBER_TYPE.PropConnect) {
- moduleFile.cmpMeta.dependencies.push(memberMeta.ctrlId);
- }
- });
- }
- return visit(tsSourceFile, moduleFile.cmpMeta) as ts.SourceFile;
+function addPropConnects(compilerCtx: CompilerCtx, buildCtx: BuildCtx, filePath: string) {
+ const moduleFile = compilerCtx.moduleFiles[filePath];
+
+ const cmpMeta = (moduleFile && moduleFile.cmpMeta);
+ if (!cmpMeta) {
+ return;
+ }
+
+ if (cmpMeta.membersMeta) {
+ Object.keys(cmpMeta.membersMeta).forEach(memberName => {
+ const memberMeta = cmpMeta.membersMeta[memberName];
+ if (memberMeta.memberType === MEMBER_TYPE.PropConnect) {
+ buildCtx.componentRefs.push({
+ tag: memberMeta.ctrlId,
+ filePath: filePath
+ });
}
- return tsSourceFile;
- };
- };
+ });
+ }
}
-function callExpression(allComponentTags: string[], cmpMeta: ComponentMeta, node: ts.CallExpression) {
+function callExpression(buildCtx: BuildCtx, allComponentTags: string[], filePath: string, node: ts.CallExpression) {
if (node && node.arguments) {
if (node.expression.kind === ts.SyntaxKind.Identifier) {
// h('tag')
- callExpressionArg(allComponentTags, cmpMeta, node.expression as ts.Identifier, node.arguments);
+ callExpressionArg(buildCtx, allComponentTags, filePath, node.expression as ts.Identifier, node.arguments);
} else if (node.expression.kind === ts.SyntaxKind.PropertyAccessExpression) {
// document.createElement('tag')
if ((node.expression as ts.PropertyAccessExpression).name) {
// const
- callExpressionArg(allComponentTags, cmpMeta, (node.expression as ts.PropertyAccessExpression).name as ts.Identifier, node.arguments);
+ callExpressionArg(buildCtx, allComponentTags, filePath, (node.expression as ts.PropertyAccessExpression).name as ts.Identifier, node.arguments);
}
}
}
}
-function callExpressionArg(allComponentTags: string[], cmpMeta: ComponentMeta, callExpressionName: ts.Identifier, args: ts.NodeArray) {
+function callExpressionArg(buildCtx: BuildCtx, allComponentTags: string[], filePath: string, callExpressionName: ts.Identifier, args: ts.NodeArray) {
if (TAG_CALL_EXPRESSIONS.includes(callExpressionName.escapedText as string)) {
if (args[0] && args[0].kind === ts.SyntaxKind.StringLiteral) {
- addDependency(allComponentTags, cmpMeta, (args[0] as ts.StringLiteral).text);
+ addComponentReference(buildCtx, allComponentTags, filePath, (args[0] as ts.StringLiteral).text);
}
}
}
-function stringLiteral(allComponentTags: string[], cmpMeta: ComponentMeta, node: ts.StringLiteral) {
+function stringLiteral(buildCtx: BuildCtx, allComponentTags: string[], filePath: string, node: ts.StringLiteral) {
let t = node.text;
if (typeof t === 'string' && t.includes('<')) {
@@ -91,18 +100,21 @@ function stringLiteral(allComponentTags: string[], cmpMeta: ComponentMeta, node:
});
foundTags.forEach(foundTag => {
- addDependency(allComponentTags, cmpMeta, foundTag);
+ addComponentReference(buildCtx, allComponentTags, filePath, foundTag);
});
}
}
-function addDependency(allComponentTags: string[], cmpMeta: ComponentMeta, tag: string) {
+function addComponentReference(buildCtx: BuildCtx, allComponentTags: string[], filePath: string, tag: string) {
if (typeof tag === 'string') {
tag = tag.toLowerCase().trim();
- if (!cmpMeta.dependencies.includes(tag) && allComponentTags.includes(tag)) {
- cmpMeta.dependencies.push(tag);
+ if (allComponentTags.includes(tag)) {
+ buildCtx.componentRefs.push({
+ tag: tag,
+ filePath: normalizePath(filePath)
+ });
}
}
}
diff --git a/src/compiler/transpile/transformers/module-graph.ts b/src/compiler/transpile/transformers/module-graph.ts
new file mode 100644
index 00000000000..fc78f99d08e
--- /dev/null
+++ b/src/compiler/transpile/transformers/module-graph.ts
@@ -0,0 +1,49 @@
+import { BuildCtx, Config, ModuleGraph } from '../../../declarations/index';
+import { normalizePath, pathJoin } from '../../util';
+import * as ts from 'typescript';
+
+
+export function moduleGraph(config: Config, buildCtx: BuildCtx): ts.TransformerFactory {
+
+ return (transformContext) => {
+
+ function visitImport(moduleGraph: ModuleGraph, dirPath: string, importNode: ts.ImportDeclaration) {
+ if (importNode.moduleSpecifier) {
+ let importPath = importNode.moduleSpecifier.getText().replace(/\'|\"|\`/g, '');
+
+ if (importPath.startsWith('.') || importPath.startsWith('/')) {
+ importPath = pathJoin(config, dirPath, importPath);
+ }
+
+ moduleGraph.importPaths.push(importPath);
+ }
+
+ return importNode;
+ }
+
+ function visit(moduleGraph: ModuleGraph, dirPath: string, node: ts.Node): ts.VisitResult {
+ switch (node.kind) {
+ case ts.SyntaxKind.ImportDeclaration:
+ return visitImport(moduleGraph, dirPath, node as ts.ImportDeclaration);
+ default:
+ return ts.visitEachChild(node, (node) => {
+ return visit(moduleGraph, dirPath, node);
+ }, transformContext);
+ }
+ }
+
+ return (tsSourceFile) => {
+ const moduleGraph: ModuleGraph = {
+ filePath: normalizePath(tsSourceFile.fileName),
+ importPaths: []
+ };
+
+ const dirPath = config.sys.path.dirname(tsSourceFile.fileName);
+
+ buildCtx.moduleGraph.push(moduleGraph);
+
+ return visit(moduleGraph, dirPath, tsSourceFile) as ts.SourceFile;
+ };
+ };
+
+}
diff --git a/src/compiler/transpile/transpile.ts b/src/compiler/transpile/transpile.ts
index 81b49ce28f4..49cc8c6c38d 100644
--- a/src/compiler/transpile/transpile.ts
+++ b/src/compiler/transpile/transpile.ts
@@ -3,11 +3,13 @@ import { BuildCtx, CompilerCtx, Config, Diagnostic, FsWriteResults, ModuleFiles,
import { componentDependencies } from './transformers/component-dependencies';
import { gatherMetadata } from './datacollection/index';
import { generateComponentTypesFile } from './create-component-types';
+import { calcComponentDependencies } from '../entries/component-dependencies';
import { getComponentsDtsSrcFilePath } from '../build/distribution';
import { getTsHost } from './compiler-host';
import { getUserTsConfig } from './compiler-options';
import { hasError, normalizePath } from '../util';
import { loadTypeScriptDiagnostics } from '../../util/logger/logger-typescript';
+import { moduleGraph } from './transformers/module-graph';
import { normalizeAssetsDir } from '../component-plugins/assets-plugin';
import { normalizeStyles } from './normalize-styles';
import { removeDecorators } from './transformers/remove-decorators';
@@ -109,15 +111,19 @@ function transpileProgram(program: ts.Program, tsHost: ts.CompilerHost, config:
program.emit(undefined, tsHost.writeFile, undefined, false, {
before: [
removeDecorators(),
- addComponentMetadata(compilerCtx.moduleFiles)
+ addComponentMetadata(compilerCtx.moduleFiles),
+ moduleGraph(config, buildCtx)
],
after: [
removeImports(),
removeStencilImports(),
- componentDependencies(compilerCtx.moduleFiles)
+ componentDependencies(compilerCtx, buildCtx)
]
});
+ // figure out how modules and components connect
+ calcComponentDependencies(compilerCtx.moduleFiles, buildCtx.moduleGraph, buildCtx.componentRefs);
+
if (!config.suppressTypeScriptErrors) {
// suppressTypeScriptErrors mainly for unit testing
const tsDiagnostics: ts.Diagnostic[] = [];
diff --git a/src/declarations/build.ts b/src/declarations/build.ts
index 0bb791cd437..8ad453b029f 100644
--- a/src/declarations/build.ts
+++ b/src/declarations/build.ts
@@ -3,6 +3,8 @@ import * as d from './index';
export interface BuildCtx {
graphData?: GraphData;
+ componentRefs?: d.ComponentReference[];
+ moduleGraph?: d.ModuleGraph[];
buildId: number;
requiresFullBuild: boolean;
diagnostics: d.Diagnostic[];
diff --git a/src/declarations/entry.ts b/src/declarations/entry.ts
index 3766b8a829c..834eaa75059 100644
--- a/src/declarations/entry.ts
+++ b/src/declarations/entry.ts
@@ -27,3 +27,13 @@ export interface EntryComponent {
tag: string;
dependencyOf?: string[];
}
+
+export interface ComponentReference {
+ tag: string;
+ filePath: string;
+}
+
+export interface ModuleGraph {
+ filePath: string;
+ importPaths: string[];
+}