in tools/example-module/generate-example-module.ts [79:192]
function analyzeExamples(sourceFiles: string[], baseDir: string): AnalyzedExamples {
const exampleMetadata: ExampleMetadata[] = [];
const exampleModules: ExampleModule[] = [];
for (const sourceFile of sourceFiles) {
const relativePath = path.relative(baseDir, sourceFile).replace(/\\/g, '/');
const importPath = relativePath.replace(/\.ts$/, '');
const packagePath = path.dirname(relativePath);
// Collect all individual example modules.
if (path.basename(sourceFile) === 'index.ts') {
exampleModules.push(
...parseExampleModuleFile(sourceFile).map(name => ({
name,
importPath,
packagePath,
})),
);
}
// Avoid parsing non-example files.
if (!path.basename(sourceFile, path.extname(sourceFile)).endsWith('-example')) {
continue;
}
const sourceContent = fs.readFileSync(sourceFile, 'utf-8');
const {primaryComponent, secondaryComponents} = parseExampleFile(sourceFile, sourceContent);
if (primaryComponent) {
// Generate a unique id for the component by converting the class name to dash-case.
const exampleId = convertToDashCase(primaryComponent.componentName.replace('Example', ''));
const example: ExampleMetadata = {
sourcePath: relativePath,
packagePath,
id: exampleId,
selector: primaryComponent.selector,
componentName: primaryComponent.componentName,
title: primaryComponent.title.trim(),
additionalComponents: [],
files: [],
module: null,
};
// For consistency, we expect the example component selector to match
// the id of the example.
const expectedSelector = `${exampleId}-example`;
if (primaryComponent.selector !== expectedSelector) {
throw Error(
`Example ${exampleId} uses selector: ${primaryComponent.selector}, ` +
`but expected: ${expectedSelector}`,
);
}
example.files.push(path.basename(relativePath));
if (primaryComponent.templateUrl) {
example.files.push(primaryComponent.templateUrl);
}
if (primaryComponent.styleUrls) {
example.files.push(...primaryComponent.styleUrls);
}
if (primaryComponent.componentName.includes('Harness')) {
example.files.push(primaryComponent.selector + '.spec.ts');
}
if (secondaryComponents.length) {
for (const meta of secondaryComponents) {
example.additionalComponents.push(meta.componentName);
if (meta.templateUrl) {
example.files.push(meta.templateUrl);
}
if (meta.styleUrls) {
example.files.push(...meta.styleUrls);
}
}
}
// Ensure referenced files actually exist in the example.
example.files.forEach(f => assertReferencedExampleFileExists(baseDir, packagePath, f));
exampleMetadata.push(example);
} else {
throw Error(
`Could not find a primary example component in ${sourceFile}. ` +
`Ensure that there's a component with an @title annotation.`,
);
}
}
// Walk through all collected examples and find their parent example module. In View Engine,
// components cannot be lazy-loaded without the associated module being loaded.
exampleMetadata.forEach(example => {
const parentModule = exampleModules.find(module =>
example.sourcePath.startsWith(module.packagePath),
);
if (!parentModule) {
throw Error(`Could not determine example module for: ${example.id}`);
}
const actualPath = path.dirname(example.sourcePath);
const expectedPath = path.posix.join(parentModule.packagePath, example.id);
// Ensures that example ids match with the directory they are stored in. This is not
// necessary for the docs site, but it helps with consistency and makes it easy to
// determine an id for an example. We also ensures for consistency that example
// folders are direct siblings of the module file.
if (actualPath !== expectedPath) {
throw Error(`Example is stored in: ${actualPath}, but expected: ${expectedPath}`);
}
example.module = parentModule;
});
return {exampleMetadata};
}