@ng-prism/core 21.10.1 → 21.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"component.scanner.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/component.scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAiBrE;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE,EACpB,OAAO,EAAE,EAAE,CAAC,WAAW,GACtB,gBAAgB,EAAE,CAyCpB"}
1
+ {"version":3,"file":"component.scanner.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/component.scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAiBrE;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE,EACpB,OAAO,EAAE,EAAE,CAAC,WAAW,GACtB,gBAAgB,EAAE,CA8CpB"}
@@ -16,6 +16,11 @@ export function scanComponents(exports, checker) {
16
16
  const classDecl = resolved.declarations?.find(ts.isClassDeclaration);
17
17
  if (!classDecl)
18
18
  continue;
19
+ // Perf: cheap pre-filter — skip the full decorator walk for files that don't
20
+ // mention Showcase at all. False positives (string-literal containing "@Showcase")
21
+ // just lose the optimization for that file; correctness is unaffected.
22
+ if (!classDecl.getSourceFile().text.includes('@Showcase'))
23
+ continue;
19
24
  const showcaseDecorator = findDecorator(classDecl, 'Showcase');
20
25
  if (!showcaseDecorator)
21
26
  continue;
@@ -1,11 +1,22 @@
1
1
  import ts from 'typescript';
2
- export interface EntryPointResult {
3
- program: ts.Program;
2
+ export interface EntryPointInput {
3
+ entryFile: string;
4
+ importPath: string;
5
+ }
6
+ export interface ResolvedEntryPoint extends EntryPointInput {
4
7
  exports: ts.Symbol[];
5
8
  }
9
+ export interface MultiEntryResult {
10
+ program: ts.Program;
11
+ entries: ResolvedEntryPoint[];
12
+ }
6
13
  /**
7
- * Resolve all exported symbols from an entry-point file (e.g. public-api.ts).
14
+ * Resolve all exported symbols for one or more entry-point files (e.g. public-api.ts).
15
+ * A single shared `ts.Program` is created with every entry file as a root name, so the
16
+ * transitive type graph (Angular framework typings, etc.) is parsed once and reused
17
+ * across all entries via the shared `TypeChecker`.
18
+ *
8
19
  * Re-exports (`export * from '...'`) are resolved automatically by TypeScript.
9
20
  */
10
- export declare function resolveEntryPointExports(entryPointPath: string, compilerOptions: ts.CompilerOptions, previousProgram?: ts.Program): EntryPointResult;
21
+ export declare function resolveEntryPointExports(entryPoints: ReadonlyArray<EntryPointInput>, compilerOptions: ts.CompilerOptions, previousProgram?: ts.Program): MultiEntryResult;
11
22
  //# sourceMappingURL=entry-point.scanner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"entry-point.scanner.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/entry-point.scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC;IACpB,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,EAAE,CAAC,eAAe,EACnC,eAAe,CAAC,EAAE,EAAE,CAAC,OAAO,GAC3B,gBAAgB,CAqBlB"}
1
+ {"version":3,"file":"entry-point.scanner.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/entry-point.scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACzD,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC;IACpB,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,aAAa,CAAC,eAAe,CAAC,EAC3C,eAAe,EAAE,EAAE,CAAC,eAAe,EACnC,eAAe,CAAC,EAAE,EAAE,CAAC,OAAO,GAC3B,gBAAgB,CAoClB"}
@@ -1,19 +1,32 @@
1
1
  import ts from 'typescript';
2
2
  /**
3
- * Resolve all exported symbols from an entry-point file (e.g. public-api.ts).
3
+ * Resolve all exported symbols for one or more entry-point files (e.g. public-api.ts).
4
+ * A single shared `ts.Program` is created with every entry file as a root name, so the
5
+ * transitive type graph (Angular framework typings, etc.) is parsed once and reused
6
+ * across all entries via the shared `TypeChecker`.
7
+ *
4
8
  * Re-exports (`export * from '...'`) are resolved automatically by TypeScript.
5
9
  */
6
- export function resolveEntryPointExports(entryPointPath, compilerOptions, previousProgram) {
7
- const program = ts.createProgram([entryPointPath], compilerOptions, undefined, previousProgram);
10
+ export function resolveEntryPointExports(entryPoints, compilerOptions, previousProgram) {
11
+ const program = ts.createProgram(entryPoints.map((e) => e.entryFile), compilerOptions, undefined, previousProgram);
8
12
  const checker = program.getTypeChecker();
9
- const sourceFile = program.getSourceFile(entryPointPath);
10
- if (!sourceFile) {
11
- throw new Error(`Could not find source file: ${entryPointPath}`);
12
- }
13
- const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
14
- if (!moduleSymbol) {
15
- return { program, exports: [] };
16
- }
17
- const exports = checker.getExportsOfModule(moduleSymbol);
18
- return { program, exports };
13
+ const entries = entryPoints.map(({ entryFile, importPath }) => {
14
+ try {
15
+ const sourceFile = program.getSourceFile(entryFile);
16
+ if (!sourceFile) {
17
+ console.warn(`⚠ ng-prism: source file not found for entry point ${entryFile}, skipping.`);
18
+ return { entryFile, importPath, exports: [] };
19
+ }
20
+ const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
21
+ const exports = moduleSymbol
22
+ ? checker.getExportsOfModule(moduleSymbol)
23
+ : [];
24
+ return { entryFile, importPath, exports };
25
+ }
26
+ catch (err) {
27
+ console.error(`⚠ ng-prism: failed to resolve exports for ${entryFile}: ${err instanceof Error ? err.message : String(err)}`);
28
+ return { entryFile, importPath, exports: [] };
29
+ }
30
+ });
31
+ return { program, entries };
19
32
  }
@@ -1,7 +1,7 @@
1
1
  export { createScanner } from './scanner.js';
2
- export type { CreateScannerOptions, Scanner } from './scanner.js';
2
+ export type { CreateScannerOptions, Scanner, ScanResult } from './scanner.js';
3
3
  export { resolveEntryPointExports } from './entry-point.scanner.js';
4
- export type { EntryPointResult } from './entry-point.scanner.js';
4
+ export type { EntryPointInput, ResolvedEntryPoint, MultiEntryResult, } from './entry-point.scanner.js';
5
5
  export { scanComponents } from './component.scanner.js';
6
6
  export { extractInputs, extractOutputs } from './input.extractor.js';
7
7
  export { generateManifest } from './manifest.generator.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,YAAY,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,oBAAoB,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -1,16 +1,25 @@
1
1
  import ts from 'typescript';
2
- import type { PrismManifest } from '../../plugin/plugin.types.js';
2
+ import type { ScannedComponent } from '../../plugin/plugin.types.js';
3
+ import { type EntryPointInput } from './entry-point.scanner.js';
3
4
  export interface CreateScannerOptions {
4
- entryPoint: string;
5
+ entryPoints: ReadonlyArray<EntryPointInput>;
5
6
  compilerOptions?: ts.CompilerOptions;
6
7
  }
8
+ export interface ScanResult {
9
+ components: ScannedComponent[];
10
+ }
7
11
  export interface Scanner {
8
- /** Scan the entry point. Reuses TypeScript program state from previous scans. */
9
- scan(): PrismManifest;
12
+ /** Scan all entry points. Reuses TypeScript program state from previous scans. */
13
+ scan(): ScanResult;
10
14
  }
11
15
  /**
12
- * Create a stateful scanner that retains the previous ts.Program between scans.
13
- * TypeScript reuses parsed SourceFile objects from the old program, making incremental scans fast.
16
+ * Create a stateful multi-entry scanner that retains the previous ts.Program between scans.
17
+ * A single shared program is created with every entry-point file as a root name, so the
18
+ * transitive type graph (Angular framework typings, etc.) is parsed once and shared via
19
+ * the program's `TypeChecker` — instead of N programs that each load Material/CDK/RxJS.
20
+ *
21
+ * TypeScript reuses parsed SourceFile objects from the old program on rebuild, making
22
+ * incremental scans fast.
14
23
  */
15
24
  export declare function createScanner(options: CreateScannerOptions): Scanner;
16
25
  //# sourceMappingURL=scanner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAIlE,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC;CACtC;AAED,MAAM,WAAW,OAAO;IACtB,iFAAiF;IACjF,IAAI,IAAI,aAAa,CAAC;CACvB;AAWD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAmBpE"}
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,0BAA0B,CAAC;AAGlC,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC5C,eAAe,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC;CACtC;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,gBAAgB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,OAAO;IACtB,kFAAkF;IAClF,IAAI,IAAI,UAAU,CAAC;CACpB;AAkBD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAoDpE"}
@@ -1,5 +1,5 @@
1
1
  import ts from 'typescript';
2
- import { resolveEntryPointExports } from './entry-point.scanner.js';
2
+ import { resolveEntryPointExports, } from './entry-point.scanner.js';
3
3
  import { scanComponents } from './component.scanner.js';
4
4
  const DEFAULT_COMPILER_OPTIONS = {
5
5
  target: ts.ScriptTarget.ES2022,
@@ -8,21 +8,58 @@ const DEFAULT_COMPILER_OPTIONS = {
8
8
  experimentalDecorators: true,
9
9
  strict: true,
10
10
  skipLibCheck: true,
11
+ // Perf: skip auto-loading every @types/* package; library source rarely needs ambient globals.
12
+ // Users with explicit needs can pass `types: ['node']` via tsconfig.json paths.
13
+ types: [],
14
+ // Perf: we never call program.emit(); skip emit-related setup work.
15
+ noEmit: true,
16
+ // Perf: complement to skipLibCheck — also skip type-checking lib.*.d.ts files.
17
+ skipDefaultLibCheck: true,
11
18
  };
12
19
  /**
13
- * Create a stateful scanner that retains the previous ts.Program between scans.
14
- * TypeScript reuses parsed SourceFile objects from the old program, making incremental scans fast.
20
+ * Create a stateful multi-entry scanner that retains the previous ts.Program between scans.
21
+ * A single shared program is created with every entry-point file as a root name, so the
22
+ * transitive type graph (Angular framework typings, etc.) is parsed once and shared via
23
+ * the program's `TypeChecker` — instead of N programs that each load Material/CDK/RxJS.
24
+ *
25
+ * TypeScript reuses parsed SourceFile objects from the old program on rebuild, making
26
+ * incremental scans fast.
15
27
  */
16
28
  export function createScanner(options) {
17
- const compilerOptions = { ...DEFAULT_COMPILER_OPTIONS, ...options.compilerOptions };
29
+ const compilerOptions = {
30
+ ...DEFAULT_COMPILER_OPTIONS,
31
+ ...options.compilerOptions,
32
+ };
18
33
  let previousProgram;
19
34
  return {
20
35
  scan() {
21
- const { program, exports } = resolveEntryPointExports(options.entryPoint, compilerOptions, previousProgram);
36
+ const { program, entries } = resolveEntryPointExports(options.entryPoints, compilerOptions, previousProgram);
22
37
  previousProgram = program;
23
38
  const checker = program.getTypeChecker();
24
- const components = scanComponents(exports, checker);
25
- return { components };
39
+ const allComponents = [];
40
+ for (const { exports, importPath } of entries) {
41
+ const components = scanComponents(exports, checker);
42
+ for (const c of components)
43
+ c.importPath = importPath;
44
+ allComponents.push(...components);
45
+ }
46
+ // Dedupe by (filePath, className): if a component class is re-exported from
47
+ // multiple entry points (e.g. atoms/button + barrel index.ts), we keep the first
48
+ // occurrence. The duplicate would otherwise produce duplicate imports in the
49
+ // generated manifest and a runtime collision in the Prism shell.
50
+ const seen = new Set();
51
+ const unique = [];
52
+ for (const c of allComponents) {
53
+ const key = `${c.filePath}::${c.className}`;
54
+ if (seen.has(key)) {
55
+ console.warn(`⚠ ng-prism: ${c.className} is exported by multiple entry points; ` +
56
+ `keeping the first occurrence (importPath: ${unique.find((u) => u.className === c.className && u.filePath === c.filePath)?.importPath}).`);
57
+ continue;
58
+ }
59
+ seen.add(key);
60
+ unique.push(c);
61
+ }
62
+ return { components: unique };
26
63
  },
27
64
  };
28
65
  }
@@ -14,7 +14,9 @@ export interface PrismPipelineResult {
14
14
  written: boolean;
15
15
  }
16
16
  export interface PrismPipelineState {
17
- scanners: Map<string, Scanner>;
17
+ scanner: Scanner | undefined;
18
+ /** Sorted, joined entry-file paths — used to detect when the entry set changed between rebuilds. */
19
+ lastEntrySetKey: string | undefined;
18
20
  }
19
21
  export declare function createPipelineState(): PrismPipelineState;
20
22
  export declare function runPrismPipeline(options: PrismPipelineOptions, context: BuilderContext, state: PrismPipelineState): Promise<PrismPipelineResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"prism-pipeline.d.ts","sourceRoot":"","sources":["../../../src/builder/shared/prism-pipeline.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAMpE,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,mGAAmG;IACnG,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,wBAAgB,mBAAmB,IAAI,kBAAkB,CAExD;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CAmD9B"}
1
+ {"version":3,"file":"prism-pipeline.d.ts","sourceRoot":"","sources":["../../../src/builder/shared/prism-pipeline.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAOpE,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,mGAAmG;IACnG,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,oGAAoG;IACpG,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,wBAAgB,mBAAmB,IAAI,kBAAkB,CAExD;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CAoD9B"}
@@ -1,5 +1,5 @@
1
1
  import { join, dirname } from 'path';
2
- import { writeFileSync, readFileSync, mkdirSync, statSync, renameSync, existsSync, unlinkSync } from 'fs';
2
+ import { writeFileSync, readFileSync, mkdirSync, statSync, renameSync, existsSync, unlinkSync, } from 'fs';
3
3
  import ts from 'typescript';
4
4
  import { createScanner } from '../scanner/scanner.js';
5
5
  import { discoverSecondaryEntryPoints } from '../scanner/entry-point-discovery.js';
@@ -7,7 +7,7 @@ import { loadConfig } from '../config-loader/config-loader.js';
7
7
  import { runPluginHooks } from '../plugin-runner/plugin-runner.js';
8
8
  import { generateRuntimeManifest } from '../manifest/runtime-manifest.generator.js';
9
9
  export function createPipelineState() {
10
- return { scanners: new Map() };
10
+ return { scanner: undefined, lastEntrySetKey: undefined };
11
11
  }
12
12
  export async function runPrismPipeline(options, context, state) {
13
13
  const { workspaceRoot } = context;
@@ -28,8 +28,8 @@ export async function runPrismPipeline(options, context, state) {
28
28
  pages: manifest.pages,
29
29
  });
30
30
  const projectMeta = await context.getProjectMetadata(options.prismProject);
31
- const sourceRoot = projectMeta['sourceRoot']
32
- ?? join('projects', options.prismProject, 'src');
31
+ const sourceRoot = projectMeta['sourceRoot'] ??
32
+ join('projects', options.prismProject, 'src');
33
33
  const manifestPath = join(workspaceRoot, sourceRoot, 'prism-manifest.ts');
34
34
  mkdirSync(join(workspaceRoot, sourceRoot), { recursive: true });
35
35
  const written = writeManifestIfChanged(manifestPath, source);
@@ -63,7 +63,9 @@ function writeManifestIfChanged(manifestPath, newContent) {
63
63
  try {
64
64
  unlinkSync(tempPath);
65
65
  }
66
- catch { /* best-effort cleanup */ }
66
+ catch {
67
+ /* best-effort cleanup */
68
+ }
67
69
  }
68
70
  throw err;
69
71
  }
@@ -77,6 +79,18 @@ function isDirectory(path) {
77
79
  return false;
78
80
  }
79
81
  }
82
+ function findLibraryRoot(filePath) {
83
+ let dir = dirname(filePath);
84
+ let parent = dirname(dir);
85
+ while (dir !== parent) {
86
+ if (existsSync(join(dir, 'ng-package.json'))) {
87
+ return dir;
88
+ }
89
+ dir = parent;
90
+ parent = dirname(dir);
91
+ }
92
+ return undefined;
93
+ }
80
94
  function resolveTsconfigPaths(entryPointDir) {
81
95
  const configPath = ts.findConfigFile(entryPointDir, ts.sys.fileExists, 'tsconfig.json');
82
96
  if (!configPath)
@@ -92,29 +106,41 @@ function resolveTsconfigPaths(entryPointDir) {
92
106
  result.baseUrl = parsed.options.baseUrl;
93
107
  return result;
94
108
  }
95
- function scanEntryPoints(workspaceRoot, options, state) {
109
+ function resolveEntryPoints(workspaceRoot, options) {
96
110
  const absoluteEntryPoint = join(workspaceRoot, options.entryPoint);
97
- const pathOptions = resolveTsconfigPaths(dirname(absoluteEntryPoint));
98
- if (!isDirectory(absoluteEntryPoint)) {
99
- return getOrCreateScanner(state, absoluteEntryPoint, pathOptions).scan();
100
- }
101
- const entryPoints = discoverSecondaryEntryPoints(absoluteEntryPoint, options.libraryImportPath);
102
- const allComponents = [];
103
- for (const ep of entryPoints) {
104
- const scanner = getOrCreateScanner(state, ep.entryFile, pathOptions);
105
- const result = scanner.scan();
106
- for (const comp of result.components) {
107
- comp.importPath = ep.importPath;
108
- allComponents.push(comp);
109
- }
110
- }
111
- return { components: allComponents };
111
+ const compilerOptions = resolveTsconfigPaths(dirname(absoluteEntryPoint));
112
+ const entryIsDirectory = isDirectory(absoluteEntryPoint);
113
+ const libraryRoot = entryIsDirectory
114
+ ? absoluteEntryPoint
115
+ : findLibraryRoot(absoluteEntryPoint);
116
+ const discovered = libraryRoot
117
+ ? discoverSecondaryEntryPoints(libraryRoot, options.libraryImportPath)
118
+ : [];
119
+ // Fallback to a direct file scan when:
120
+ // - the entry is a file and no ng-package.json was found above it, OR
121
+ // - the entry is a file with an ng-package.json above it but discovery found nothing
122
+ // (e.g. primary-only library, root ng-package.json carries `dest`).
123
+ const entryPoints = discovered.length === 0 && !entryIsDirectory
124
+ ? [
125
+ {
126
+ entryFile: absoluteEntryPoint,
127
+ importPath: options.libraryImportPath,
128
+ },
129
+ ]
130
+ : discovered;
131
+ return { entryPoints, compilerOptions };
112
132
  }
113
- function getOrCreateScanner(state, entryFile, compilerOptions) {
114
- let scanner = state.scanners.get(entryFile);
115
- if (!scanner) {
116
- scanner = createScanner({ entryPoint: entryFile, compilerOptions });
117
- state.scanners.set(entryFile, scanner);
133
+ function scanEntryPoints(workspaceRoot, options, state) {
134
+ const { entryPoints, compilerOptions } = resolveEntryPoints(workspaceRoot, options);
135
+ const newKey = entryPoints
136
+ .map((e) => e.entryFile)
137
+ .slice()
138
+ .sort()
139
+ .join('|');
140
+ if (newKey !== state.lastEntrySetKey) {
141
+ state.scanner = undefined;
142
+ state.lastEntrySetKey = newKey;
118
143
  }
119
- return scanner;
144
+ state.scanner ??= createScanner({ entryPoints, compilerOptions });
145
+ return state.scanner.scan();
120
146
  }
@@ -141,8 +141,7 @@ function addBuilderTargets(options) {
141
141
  const project = workspace.projects[options.project];
142
142
  const prismProjectName = `${options.project}-prism`;
143
143
  const port = options.port ?? 4400;
144
- const sourceRoot = project.sourceRoot ?? `${project.root}/src`;
145
- const entryPoint = `${sourceRoot}/public-api.ts`;
144
+ const entryPoint = project.root ?? project.sourceRoot ?? `projects/${options.project}`;
146
145
  if (!project.architect) {
147
146
  project.architect = {};
148
147
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-prism/core",
3
- "version": "21.10.1",
3
+ "version": "21.10.3",
4
4
  "description": "ng-prism — the Angular-native Storybook alternative. Lightweight component showcase with @Showcase decorator, no story files needed.",
5
5
  "keywords": [
6
6
  "ng-prism",