@ng-prism/core 22.5.0 → 22.6.0

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.
Files changed (118) hide show
  1. package/README.md +2 -0
  2. package/dist/app/canvas/prism-canvas-toolbar.component.d.ts +22 -1
  3. package/dist/app/canvas/prism-canvas-toolbar.component.d.ts.map +1 -1
  4. package/dist/app/canvas/prism-canvas-toolbar.component.js +111 -38
  5. package/dist/app/canvas/state-chips.d.ts +50 -0
  6. package/dist/app/canvas/state-chips.d.ts.map +1 -0
  7. package/dist/app/canvas/state-chips.js +40 -0
  8. package/dist/app/component-head/prism-component-head.component.d.ts +1 -0
  9. package/dist/app/component-head/prism-component-head.component.d.ts.map +1 -1
  10. package/dist/app/component-head/prism-component-head.component.js +11 -6
  11. package/dist/app/component-head/prism-head-info.component.d.ts +2 -1
  12. package/dist/app/component-head/prism-head-info.component.d.ts.map +1 -1
  13. package/dist/app/component-head/prism-head-info.component.js +30 -7
  14. package/dist/app/directives/resize-value.d.ts +1 -1
  15. package/dist/app/directives/resize-value.js +1 -1
  16. package/dist/app/discovery-manifest.d.ts +2 -2
  17. package/dist/app/discovery-manifest.d.ts.map +1 -1
  18. package/dist/app/discovery-manifest.js +15 -3
  19. package/dist/app/overview/open-in-playground.d.ts +11 -0
  20. package/dist/app/overview/open-in-playground.d.ts.map +1 -0
  21. package/dist/app/overview/open-in-playground.js +12 -0
  22. package/dist/app/overview/overview-matrix.d.ts +79 -0
  23. package/dist/app/overview/overview-matrix.d.ts.map +1 -0
  24. package/dist/app/overview/overview-matrix.js +98 -0
  25. package/dist/app/overview/overview-visibility.d.ts +10 -2
  26. package/dist/app/overview/overview-visibility.d.ts.map +1 -1
  27. package/dist/app/overview/overview-visibility.js +14 -3
  28. package/dist/app/overview/prism-overview-cell.component.d.ts +23 -2
  29. package/dist/app/overview/prism-overview-cell.component.d.ts.map +1 -1
  30. package/dist/app/overview/prism-overview-cell.component.js +133 -27
  31. package/dist/app/overview/prism-overview-matrix.component.d.ts +29 -0
  32. package/dist/app/overview/prism-overview-matrix.component.d.ts.map +1 -0
  33. package/dist/app/overview/prism-overview-matrix.component.js +164 -0
  34. package/dist/app/overview/prism-overview.component.d.ts +4 -0
  35. package/dist/app/overview/prism-overview.component.d.ts.map +1 -1
  36. package/dist/app/overview/prism-overview.component.js +37 -14
  37. package/dist/app/panels/a11y/a11y-core.plugin.d.ts.map +1 -1
  38. package/dist/app/panels/a11y/a11y-core.plugin.js +0 -1
  39. package/dist/app/panels/controls/controls-plugin.d.ts.map +1 -1
  40. package/dist/app/panels/controls/controls-plugin.js +1 -2
  41. package/dist/app/panels/events/events-plugin.d.ts.map +1 -1
  42. package/dist/app/panels/events/events-plugin.js +1 -2
  43. package/dist/app/pseudo/pseudo-state-marker.d.ts +32 -10
  44. package/dist/app/pseudo/pseudo-state-marker.d.ts.map +1 -1
  45. package/dist/app/pseudo/pseudo-state-marker.js +83 -67
  46. package/dist/app/pseudo/pseudo-targets.d.ts +11 -0
  47. package/dist/app/pseudo/pseudo-targets.d.ts.map +1 -0
  48. package/dist/app/pseudo/pseudo-targets.js +46 -0
  49. package/dist/app/pseudo/state-fit.d.ts +28 -0
  50. package/dist/app/pseudo/state-fit.d.ts.map +1 -0
  51. package/dist/app/pseudo/state-fit.js +97 -0
  52. package/dist/app/renderer/input-bindings.d.ts +16 -0
  53. package/dist/app/renderer/input-bindings.d.ts.map +1 -0
  54. package/dist/app/renderer/input-bindings.js +21 -0
  55. package/dist/app/renderer/known-inputs.d.ts +8 -1
  56. package/dist/app/renderer/known-inputs.d.ts.map +1 -1
  57. package/dist/app/renderer/known-inputs.js +13 -5
  58. package/dist/app/renderer/prism-renderer.component.d.ts +10 -3
  59. package/dist/app/renderer/prism-renderer.component.d.ts.map +1 -1
  60. package/dist/app/renderer/prism-renderer.component.js +51 -29
  61. package/dist/app/renderer/projectable-content.d.ts +8 -1
  62. package/dist/app/renderer/projectable-content.d.ts.map +1 -1
  63. package/dist/app/renderer/projectable-content.js +40 -29
  64. package/dist/app/renderer/rendered-key.d.ts +20 -6
  65. package/dist/app/renderer/rendered-key.d.ts.map +1 -1
  66. package/dist/app/renderer/rendered-key.js +25 -8
  67. package/dist/app/renderer/snippet-generator.js +7 -5
  68. package/dist/app/services/prism-pseudo-state.service.d.ts +59 -14
  69. package/dist/app/services/prism-pseudo-state.service.d.ts.map +1 -1
  70. package/dist/app/services/prism-pseudo-state.service.js +86 -29
  71. package/dist/app/services/prism-renderer.service.d.ts +17 -0
  72. package/dist/app/services/prism-renderer.service.d.ts.map +1 -1
  73. package/dist/app/services/prism-renderer.service.js +37 -2
  74. package/dist/app/services/prism-url-state.service.d.ts.map +1 -1
  75. package/dist/app/services/prism-url-state.service.js +39 -8
  76. package/dist/app/theme/prism-default-theme.d.ts.map +1 -1
  77. package/dist/app/theme/prism-default-theme.js +6 -5
  78. package/dist/builder/config-loader/deprecated-options.d.ts +7 -0
  79. package/dist/builder/config-loader/deprecated-options.d.ts.map +1 -0
  80. package/dist/builder/config-loader/deprecated-options.js +23 -0
  81. package/dist/builder/manifest/runtime-manifest.generator.d.ts.map +1 -1
  82. package/dist/builder/manifest/runtime-manifest.generator.js +3 -2
  83. package/dist/builder/scanner/component.scanner.d.ts.map +1 -1
  84. package/dist/builder/scanner/component.scanner.js +20 -1
  85. package/dist/builder/scanner/entry-point-discovery.d.ts +5 -0
  86. package/dist/builder/scanner/entry-point-discovery.d.ts.map +1 -1
  87. package/dist/builder/scanner/entry-point-discovery.js +14 -0
  88. package/dist/builder/scanner/entry-point.scanner.d.ts +3 -1
  89. package/dist/builder/scanner/entry-point.scanner.d.ts.map +1 -1
  90. package/dist/builder/scanner/entry-point.scanner.js +7 -6
  91. package/dist/builder/scanner/input.extractor.d.ts.map +1 -1
  92. package/dist/builder/scanner/input.extractor.js +22 -3
  93. package/dist/builder/scanner/pseudo-states.d.ts +1 -0
  94. package/dist/builder/scanner/pseudo-states.d.ts.map +1 -1
  95. package/dist/builder/scanner/pseudo-states.js +1 -1
  96. package/dist/builder/scanner/scanner.d.ts +2 -2
  97. package/dist/builder/scanner/scanner.d.ts.map +1 -1
  98. package/dist/builder/scanner/scanner.js +16 -6
  99. package/dist/builder/scanner/showcase-diagnostics.js +9 -9
  100. package/dist/builder/scanner/showcase-states.d.ts +17 -0
  101. package/dist/builder/scanner/showcase-states.d.ts.map +1 -0
  102. package/dist/builder/scanner/showcase-states.js +58 -0
  103. package/dist/builder/scanner/source-file-cache.d.ts +26 -0
  104. package/dist/builder/scanner/source-file-cache.d.ts.map +1 -0
  105. package/dist/builder/scanner/source-file-cache.js +60 -0
  106. package/dist/builder/shared/prism-pipeline.d.ts.map +1 -1
  107. package/dist/builder/shared/prism-pipeline.js +28 -13
  108. package/dist/decorator/index.d.ts +1 -1
  109. package/dist/decorator/index.d.ts.map +1 -1
  110. package/dist/decorator/showcase.types.d.ts +23 -0
  111. package/dist/decorator/showcase.types.d.ts.map +1 -1
  112. package/dist/plugin/index.d.ts +1 -1
  113. package/dist/plugin/index.d.ts.map +1 -1
  114. package/dist/plugin/plugin.types.d.ts +44 -5
  115. package/dist/plugin/plugin.types.d.ts.map +1 -1
  116. package/package.json +1 -1
  117. package/src/builder/build/schema.json +6 -6
  118. package/src/builder/serve/schema.json +6 -6
@@ -4,8 +4,9 @@ import { CANVAS_LAYOUTS } from '../../shared/canvas-layout.type.js';
4
4
  import { evaluateExpression, evaluateStatic, findDecorator, getDecoratorArgument, UNEVALUABLE } from './ast-utils.js';
5
5
  import { getClassHierarchy } from './class-hierarchy.js';
6
6
  import { extractInputs, extractOutputs } from './input.extractor.js';
7
- import { readPseudoStates } from './pseudo-states.js';
7
+ import { describeValue, readPseudoStates } from './pseudo-states.js';
8
8
  import { describeDeprecatedProviders, describeUnevaluable, describeUnreadableBase } from './showcase-diagnostics.js';
9
+ import { readShowcaseStates } from './showcase-states.js';
9
10
  const COMPONENT_STATUSES = ['stable', 'beta', 'wip', 'deprecated'];
10
11
  function isComponentStatus(value) {
11
12
  return typeof value === 'string' && COMPONENT_STATUSES.includes(value);
@@ -123,6 +124,8 @@ function extractShowcaseConfig(decorator, className, report, checker) {
123
124
  return undefined;
124
125
  // Reported once for the whole field below, wherever it came from: the literal, a spread or a constant.
125
126
  let providers;
127
+ // A state the evaluator dropped whole leaves no element behind, so its position is noted to keep the later states where the source has them.
128
+ const unevaluableStates = new Set();
126
129
  const raw = evaluateStatic(arg, {
127
130
  checker,
128
131
  report: (issue) => {
@@ -130,6 +133,8 @@ function extractShowcaseConfig(decorator, className, report, checker) {
130
133
  providers ??= issue.node;
131
134
  return;
132
135
  }
136
+ if (issue.path[0] === 'states' && issue.path.length === 2 && typeof issue.path[1] === 'number')
137
+ unevaluableStates.add(issue.path[1]);
133
138
  report(describeUnevaluable(className, arg, issue));
134
139
  }
135
140
  });
@@ -169,6 +174,16 @@ function extractShowcaseConfig(decorator, className, report, checker) {
169
174
  config.host = obj['host'];
170
175
  if (obj['renderPage'])
171
176
  config.renderPage = obj['renderPage'];
177
+ if (obj['states'] !== undefined) {
178
+ if (config.renderPage) {
179
+ report(`${className} declares states together with renderPage; a component page has no Overview to show them in. Skipping states.`);
180
+ }
181
+ else {
182
+ const states = readShowcaseStates(obj['states'], className, report, unevaluableStates);
183
+ if (states)
184
+ config.states = states;
185
+ }
186
+ }
172
187
  if (obj['status'] !== undefined) {
173
188
  if (isComponentStatus(obj['status'])) {
174
189
  config.status = obj['status'];
@@ -218,6 +233,10 @@ function extractShowcaseConfig(decorator, className, report, checker) {
218
233
  else
219
234
  delete cleaned['pseudo'];
220
235
  }
236
+ if (variant['states'] !== undefined && variant['states'] !== false) {
237
+ report(`${className} variant "${String(variant['name'])}" declares invalid states ${describeValue(variant['states'])}, expected false. Skipping.`);
238
+ delete cleaned['states'];
239
+ }
221
240
  return cleaned;
222
241
  });
223
242
  }
@@ -3,4 +3,9 @@ export interface DiscoveredEntryPoint {
3
3
  importPath: string;
4
4
  }
5
5
  export declare function discoverSecondaryEntryPoints(libraryRoot: string, baseImportPath: string): DiscoveredEntryPoint[];
6
+ /**
7
+ * The primary entry file of the library at `libraryRoot`: `lib.entryFile` of its
8
+ * own `ng-package.json`, or `undefined` when there is no such file.
9
+ */
10
+ export declare function findPrimaryEntryFile(libraryRoot: string): string | undefined;
6
11
  //# sourceMappingURL=entry-point-discovery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"entry-point-discovery.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/entry-point-discovery.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,oBAAoB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAKhH"}
1
+ {"version":3,"file":"entry-point-discovery.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/entry-point-discovery.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,oBAAoB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,4BAA4B,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,oBAAoB,EAAE,CAKhH;AAKD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAS5E"}
@@ -5,6 +5,20 @@ export function discoverSecondaryEntryPoints(libraryRoot, baseImportPath) {
5
5
  findNgPackageJsons(libraryRoot, libraryRoot, baseImportPath, entryPoints);
6
6
  return entryPoints;
7
7
  }
8
+ /** ng-packagr's default for `lib.entryFile`. */
9
+ const NG_PACKAGR_DEFAULT_ENTRY_FILE = 'src/public_api.ts';
10
+ /**
11
+ * The primary entry file of the library at `libraryRoot`: `lib.entryFile` of its
12
+ * own `ng-package.json`, or `undefined` when there is no such file.
13
+ */
14
+ export function findPrimaryEntryFile(libraryRoot) {
15
+ const ngPackagePath = join(libraryRoot, 'ng-package.json');
16
+ if (!existsSync(ngPackagePath))
17
+ return undefined;
18
+ const ngPackage = JSON.parse(readFileSync(ngPackagePath, 'utf-8'));
19
+ const entryFilePath = join(libraryRoot, ngPackage.lib?.entryFile ?? NG_PACKAGR_DEFAULT_ENTRY_FILE);
20
+ return existsSync(entryFilePath) ? entryFilePath : undefined;
21
+ }
8
22
  function findNgPackageJsons(dir, libraryRoot, baseImportPath, result) {
9
23
  const isLibraryRoot = dir === libraryRoot;
10
24
  const ngPackagePath = join(dir, 'ng-package.json');
@@ -2,6 +2,8 @@ import ts from 'typescript';
2
2
  export interface EntryPointInput {
3
3
  entryFile: string;
4
4
  importPath: string;
5
+ /** The library's primary entry point. A component it re-exports from a secondary one keeps that one, silently. */
6
+ primary?: boolean;
5
7
  }
6
8
  export interface ResolvedEntryPoint extends EntryPointInput {
7
9
  exports: ts.Symbol[];
@@ -18,5 +20,5 @@ export interface MultiEntryResult {
18
20
  *
19
21
  * Re-exports (`export * from '...'`) are resolved automatically by TypeScript.
20
22
  */
21
- export declare function resolveEntryPointExports(entryPoints: ReadonlyArray<EntryPointInput>, compilerOptions: ts.CompilerOptions, previousProgram?: ts.Program): MultiEntryResult;
23
+ export declare function resolveEntryPointExports(entryPoints: ReadonlyArray<EntryPointInput>, compilerOptions: ts.CompilerOptions, previousProgram?: ts.Program, host?: ts.CompilerHost): MultiEntryResult;
22
24
  //# 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,eAAe;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACvD,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC;IACpB,OAAO,EAAE,kBAAkB,EAAE,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACpC,WAAW,EAAE,aAAa,CAAC,eAAe,CAAC,EAC3C,eAAe,EAAE,EAAE,CAAC,eAAe,EACnC,eAAe,CAAC,EAAE,EAAE,CAAC,OAAO,GAC7B,gBAAgB,CA4BlB"}
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;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,kHAAkH;IAClH,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACvD,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC;IACpB,OAAO,EAAE,kBAAkB,EAAE,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACpC,WAAW,EAAE,aAAa,CAAC,eAAe,CAAC,EAC3C,eAAe,EAAE,EAAE,CAAC,eAAe,EACnC,eAAe,CAAC,EAAE,EAAE,CAAC,OAAO,EAC5B,IAAI,CAAC,EAAE,EAAE,CAAC,YAAY,GACvB,gBAAgB,CA8BlB"}
@@ -7,23 +7,24 @@ import ts from 'typescript';
7
7
  *
8
8
  * Re-exports (`export * from '...'`) are resolved automatically by TypeScript.
9
9
  */
10
- export function resolveEntryPointExports(entryPoints, compilerOptions, previousProgram) {
11
- const program = ts.createProgram(entryPoints.map((e) => e.entryFile), compilerOptions, undefined, previousProgram);
10
+ export function resolveEntryPointExports(entryPoints, compilerOptions, previousProgram, host) {
11
+ const program = ts.createProgram(entryPoints.map((e) => e.entryFile), compilerOptions, host, previousProgram);
12
12
  const checker = program.getTypeChecker();
13
- const entries = entryPoints.map(({ entryFile, importPath }) => {
13
+ const entries = entryPoints.map((entryPoint) => {
14
+ const { entryFile } = entryPoint;
14
15
  try {
15
16
  const sourceFile = program.getSourceFile(entryFile);
16
17
  if (!sourceFile) {
17
18
  console.warn(`⚠ ng-prism: source file not found for entry point ${entryFile}, skipping.`);
18
- return { entryFile, importPath, exports: [] };
19
+ return { ...entryPoint, exports: [] };
19
20
  }
20
21
  const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
21
22
  const exports = moduleSymbol ? checker.getExportsOfModule(moduleSymbol) : [];
22
- return { entryFile, importPath, exports };
23
+ return { ...entryPoint, exports };
23
24
  }
24
25
  catch (err) {
25
26
  console.error(`⚠ ng-prism: failed to resolve exports for ${entryFile}: ${err instanceof Error ? err.message : String(err)}`);
26
- return { entryFile, importPath, exports: [] };
27
+ return { ...entryPoint, exports: [] };
27
28
  }
28
29
  });
29
30
  return { program, entries };
@@ -1 +1 @@
1
- {"version":3,"file":"input.extractor.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/input.extractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAI1E;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,EAAE,CAAC,gBAAgB,EAAE,OAAO,EAAE,EAAE,CAAC,WAAW,EAAE,SAAS,gDAAwC,GAAG,SAAS,EAAE,CAuCrJ;AAsED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,EAAE,CAAC,gBAAgB,EAAE,OAAO,EAAE,EAAE,CAAC,WAAW,EAAE,SAAS,gDAAwC,GAAG,UAAU,EAAE,CAuBvJ"}
1
+ {"version":3,"file":"input.extractor.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/input.extractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAI1E;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,EAAE,CAAC,gBAAgB,EAAE,OAAO,EAAE,EAAE,CAAC,WAAW,EAAE,SAAS,gDAAwC,GAAG,SAAS,EAAE,CAuCrJ;AAwFD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,EAAE,CAAC,gBAAgB,EAAE,OAAO,EAAE,EAAE,CAAC,WAAW,EAAE,SAAS,gDAAwC,GAAG,UAAU,EAAE,CA4BvJ"}
@@ -1,5 +1,5 @@
1
1
  import ts from 'typescript';
2
- import { evaluateStatic, findDecorator, getDecoratorArgument, getJsDocComment } from './ast-utils.js';
2
+ import { evaluateExpression, evaluateStatic, findDecorator, getDecoratorArgument, getJsDocComment } from './ast-utils.js';
3
3
  import { getClassHierarchy } from './class-hierarchy.js';
4
4
  /**
5
5
  * Extract all @Input(), input() and model() signal metadata from a class
@@ -58,12 +58,14 @@ function readInput(member, name, checker, owner, subclass) {
58
58
  const inputDecorator = findDecorator(member, 'Input');
59
59
  if (inputDecorator) {
60
60
  const required = isDecoratorInputRequired(inputDecorator);
61
+ const alias = readAlias(getDecoratorArgument(inputDecorator), name, checker);
61
62
  const defaultValue = readInitialValue(member, checker);
62
63
  const doc = getJsDocComment(member, checker);
63
64
  const instantiated = subclass && usesTypeParameters(member.type ?? member.initializer, owner, checker) ? getInstantiatedType(name, subclass, checker) : undefined;
64
65
  const { type, values, rawType } = instantiated ? mapType(instantiated, checker) : resolveDecoratorInputType(member, checker);
65
66
  return {
66
67
  name,
68
+ ...(alias && { alias }),
67
69
  type,
68
70
  rawType,
69
71
  required,
@@ -76,6 +78,8 @@ function readInput(member, name, checker, owner, subclass) {
76
78
  if (!signalCall)
77
79
  return undefined;
78
80
  const required = isSignalInputRequired(signalCall);
81
+ // `input(initial, options)`, but `input.required(options)`.
82
+ const alias = readAlias(signalCall.arguments[required ? 0 : 1], name, checker);
79
83
  const defaultValue = readInitialValue(member, checker);
80
84
  const doc = getJsDocComment(member, checker);
81
85
  const declaredType = signalCall.typeArguments?.[0] ?? signalCall.arguments[0];
@@ -84,6 +88,7 @@ function readInput(member, name, checker, owner, subclass) {
84
88
  const { type, values, rawType } = instantiated ? mapType(instantiated, checker) : resolveSignalInputType(signalCall, checker);
85
89
  return {
86
90
  name,
91
+ ...(alias && { alias }),
87
92
  type,
88
93
  rawType,
89
94
  required,
@@ -92,6 +97,17 @@ function readInput(member, name, checker, owner, subclass) {
92
97
  ...(doc && { doc })
93
98
  };
94
99
  }
100
+ /**
101
+ * The `alias` of an input or output, from a decorator argument (`'x'` or
102
+ * `{ alias: 'x' }`) or the options of `input()`, `model()` and `output()`.
103
+ * It counts when the builder can evaluate it statically to a string (a literal
104
+ * or a constant) that differs from the member name.
105
+ */
106
+ function readAlias(arg, name, checker) {
107
+ const value = arg ? evaluateExpression(arg, checker) : undefined;
108
+ const alias = typeof value === 'object' && value !== null ? value['alias'] : value;
109
+ return typeof alias === 'string' && alias !== name ? alias : undefined;
110
+ }
95
111
  /**
96
112
  * Extract all @Output() and output() signal metadata from a class declaration
97
113
  * and the base classes Angular inherits outputs from.
@@ -108,11 +124,14 @@ export function extractOutputs(classDecl, checker, hierarchy = getClassHierarchy
108
124
  const name = member.name && ts.isIdentifier(member.name) ? member.name.text : undefined;
109
125
  if (!name || seen.has(name))
110
126
  continue;
111
- if (!findDecorator(member, 'Output') && !isOutputSignal(member))
127
+ const outputDecorator = findDecorator(member, 'Output');
128
+ if (!outputDecorator && !isOutputSignal(member))
112
129
  continue;
113
130
  const doc = getJsDocComment(member, checker);
131
+ const options = outputDecorator ? getDecoratorArgument(outputDecorator) : member.initializer.arguments[0];
132
+ const alias = readAlias(options, name, checker);
114
133
  seen.add(name);
115
- outputs.push({ name, ...(doc && { doc }) });
134
+ outputs.push({ name, ...(alias && { alias }), ...(doc && { doc }) });
116
135
  }
117
136
  }
118
137
  return outputs;
@@ -1,4 +1,5 @@
1
1
  import { type ForcedPseudoStates } from '../../shared/pseudo-state.type.js';
2
+ export declare function describeValue(value: unknown): string;
2
3
  /**
3
4
  * `Variant.pseudo` as the scanner evaluated it, with everything it cannot use
4
5
  * reported and dropped: a value that is no object, an unknown state, and a
@@ -1 +1 @@
1
- {"version":3,"file":"pseudo-states.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/pseudo-states.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,kBAAkB,EAAsD,MAAM,mCAAmC,CAAC;AAehI;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,kBAAkB,GAAG,SAAS,CAmBjI"}
1
+ {"version":3,"file":"pseudo-states.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/pseudo-states.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,kBAAkB,EAAsD,MAAM,mCAAmC,CAAC;AAUhI,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGpD;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,kBAAkB,GAAG,SAAS,CAmBjI"}
@@ -5,7 +5,7 @@ function isPseudoState(value) {
5
5
  function isSelector(value) {
6
6
  return typeof value === 'string' && value.trim() !== '';
7
7
  }
8
- function describeValue(value) {
8
+ export function describeValue(value) {
9
9
  if (typeof value === 'string' || (typeof value === 'object' && value !== null))
10
10
  return JSON.stringify(value) ?? String(value);
11
11
  return String(value);
@@ -20,8 +20,8 @@ export interface Scanner {
20
20
  * transitive type graph (Angular framework typings, etc.) is parsed once and shared via
21
21
  * the program's `TypeChecker`, instead of N programs that each load Material/CDK/RxJS.
22
22
  *
23
- * TypeScript reuses parsed SourceFile objects from the old program on rebuild, making
24
- * incremental scans fast.
23
+ * On rebuild, the source-file cache hands TypeScript the already parsed SourceFile of
24
+ * every unchanged file, so only the changed files are parsed again.
25
25
  */
26
26
  export declare function createScanner(options: CreateScannerOptions): Scanner;
27
27
  //# 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,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,OAAO,EAAE,KAAK,eAAe,EAA4B,MAAM,0BAA0B,CAAC;AAE1F,MAAM,WAAW,oBAAoB;IACjC,WAAW,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC5C,eAAe,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC;CACxC;AAED,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,0FAA0F;IAC1F,WAAW,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,OAAO;IACpB,kFAAkF;IAClF,IAAI,IAAI,UAAU,CAAC;CACtB;AAkBD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAiDpE"}
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;AAErE,OAAO,EAAE,KAAK,eAAe,EAA4B,MAAM,0BAA0B,CAAC;AAG1F,MAAM,WAAW,oBAAoB;IACjC,WAAW,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IAC5C,eAAe,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC;CACxC;AAED,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,0FAA0F;IAC1F,WAAW,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,OAAO;IACpB,kFAAkF;IAClF,IAAI,IAAI,UAAU,CAAC;CACtB;AAkBD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CA0DpE"}
@@ -1,6 +1,7 @@
1
1
  import ts from 'typescript';
2
2
  import { scanComponents } from './component.scanner.js';
3
3
  import { resolveEntryPointExports } from './entry-point.scanner.js';
4
+ import { createSourceFileCache } from './source-file-cache.js';
4
5
  const DEFAULT_COMPILER_OPTIONS = {
5
6
  target: ts.ScriptTarget.ES2022,
6
7
  module: ts.ModuleKind.ES2022,
@@ -22,38 +23,47 @@ const DEFAULT_COMPILER_OPTIONS = {
22
23
  * transitive type graph (Angular framework typings, etc.) is parsed once and shared via
23
24
  * the program's `TypeChecker`, instead of N programs that each load Material/CDK/RxJS.
24
25
  *
25
- * TypeScript reuses parsed SourceFile objects from the old program on rebuild, making
26
- * incremental scans fast.
26
+ * On rebuild, the source-file cache hands TypeScript the already parsed SourceFile of
27
+ * every unchanged file, so only the changed files are parsed again.
27
28
  */
28
29
  export function createScanner(options) {
29
30
  const compilerOptions = {
30
31
  ...DEFAULT_COMPILER_OPTIONS,
31
32
  ...options.compilerOptions
32
33
  };
34
+ const sourceFiles = createSourceFileCache(compilerOptions);
33
35
  let previousProgram;
34
36
  return {
35
37
  scan() {
36
- const { program, entries } = resolveEntryPointExports(options.entryPoints, compilerOptions, previousProgram);
38
+ const { program, entries } = resolveEntryPointExports(options.entryPoints, compilerOptions, previousProgram, sourceFiles.host);
37
39
  previousProgram = program;
40
+ sourceFiles.prune(program);
38
41
  const checker = program.getTypeChecker();
39
42
  const allComponents = [];
40
43
  const diagnostics = [];
41
44
  const notices = new Set();
42
- for (const { exports, importPath } of entries) {
45
+ const fromPrimary = new Set();
46
+ for (const { exports, importPath, primary } of entries) {
43
47
  const components = scanComponents(exports, checker, diagnostics, notices);
44
- for (const c of components)
48
+ for (const c of components) {
45
49
  c.importPath = importPath;
50
+ if (primary)
51
+ fromPrimary.add(c);
52
+ }
46
53
  allComponents.push(...components);
47
54
  }
48
55
  // Dedupe by (filePath, className): if a component class is re-exported from
49
56
  // multiple entry points (e.g. atoms/button + barrel index.ts), we keep the first
50
57
  // occurrence. The duplicate would otherwise produce duplicate imports in the
51
- // generated manifest and a runtime collision in the Prism shell.
58
+ // generated manifest and a runtime collision in the Prism shell. The primary
59
+ // entry point comes last and re-exporting its secondaries is normal there.
52
60
  const seen = new Set();
53
61
  const unique = [];
54
62
  for (const c of allComponents) {
55
63
  const key = `${c.filePath}::${c.className}`;
56
64
  if (seen.has(key)) {
65
+ if (fromPrimary.has(c))
66
+ continue;
57
67
  console.warn(`⚠ ng-prism: ${c.className} is exported by multiple entry points; ` +
58
68
  `keeping the first occurrence (importPath: ${unique.find((u) => u.className === c.className && u.filePath === c.filePath)?.importPath}).`);
59
69
  continue;
@@ -49,14 +49,14 @@ export function describeUnreadableBase(className, base) {
49
49
  return (`${className} inherits ${base.inputs.join(', ')} from ${baseName}, which only exists as a declaration (${location(base.classDecl)}); ` +
50
50
  `without its source these inputs cannot be read, so they are missing from the manifest and variants cannot set them.`);
51
51
  }
52
- /** A variant is named by its `name` as well as its index, since that is what the styleguide shows. */
52
+ /** A variant or a state is named by its `name` as well as its index, since that is what the styleguide shows. */
53
53
  function formatShowcasePath(path, config) {
54
54
  const [head, index, ...rest] = path;
55
- if (head !== 'variants' || typeof index !== 'number')
55
+ if ((head !== 'variants' && head !== 'states') || typeof index !== 'number')
56
56
  return formatPath(path);
57
- const name = variantName(config, index);
58
- const variant = name === undefined ? `variants[${index}]` : `variants[${index}] ${JSON.stringify(name)}`;
59
- return rest.length === 0 ? variant : `${variant} › ${formatPath(rest)}`;
57
+ const name = elementName(config, head, index);
58
+ const element = name === undefined ? `${head}[${index}]` : `${head}[${index}] ${JSON.stringify(name)}`;
59
+ return rest.length === 0 ? element : `${element} › ${formatPath(rest)}`;
60
60
  }
61
61
  function formatPath(path) {
62
62
  return path
@@ -70,10 +70,10 @@ function formatPath(path) {
70
70
  .join('');
71
71
  }
72
72
  /** Read from the source, because the evaluated array has already lost the elements before the index. */
73
- function variantName(config, index) {
74
- const variants = ts.isObjectLiteralExpression(config) ? findInitializer(config, 'variants') : undefined;
75
- const variant = variants && ts.isArrayLiteralExpression(variants) ? variants.elements[index] : undefined;
76
- const name = variant && ts.isObjectLiteralExpression(variant) ? findInitializer(variant, 'name') : undefined;
73
+ function elementName(config, key, index) {
74
+ const elements = ts.isObjectLiteralExpression(config) ? findInitializer(config, key) : undefined;
75
+ const element = elements && ts.isArrayLiteralExpression(elements) ? elements.elements[index] : undefined;
76
+ const name = element && ts.isObjectLiteralExpression(element) ? findInitializer(element, 'name') : undefined;
77
77
  const value = name && evaluateExpression(name);
78
78
  return typeof value === 'string' ? value : undefined;
79
79
  }
@@ -0,0 +1,17 @@
1
+ import type { ShowcaseState } from '../../decorator/showcase.types.js';
2
+ /**
3
+ * `ShowcaseConfig.states` as the scanner evaluated it, with everything it
4
+ * cannot use reported. `undefined` when it is no list, or an empty one.
5
+ *
6
+ * Every slot of the list survives, because a state is identified by its
7
+ * position (the URL, the marker and the discovery manifest use it): an
8
+ * unusable entry becomes a state without pseudo states, which the styleguide
9
+ * shows as an empty column, instead of shifting the entries behind it.
10
+ * A state without a usable name is called `State <position>`.
11
+ *
12
+ * `unevaluable` holds the source positions of the entries the evaluator could
13
+ * not read at all. They are missing from `value`, so an empty slot goes back
14
+ * in at each of them, without a message of its own.
15
+ */
16
+ export declare function readShowcaseStates(value: unknown, className: string, report: (message: string) => void, unevaluable?: ReadonlySet<number>): ShowcaseState[] | undefined;
17
+ //# sourceMappingURL=showcase-states.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"showcase-states.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/showcase-states.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAUvE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAC9B,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EACjC,WAAW,GAAE,WAAW,CAAC,MAAM,CAAa,GAC7C,aAAa,EAAE,GAAG,SAAS,CA4C7B"}
@@ -0,0 +1,58 @@
1
+ import { describeValue, readPseudoStates } from './pseudo-states.js';
2
+ function isEmptyObject(value) {
3
+ return typeof value === 'object' && value !== null && !Array.isArray(value) && Object.keys(value).length === 0;
4
+ }
5
+ /** Stands in for a state the evaluator dropped, which its own diagnostic already explains. */
6
+ const GAP = Symbol('unevaluable state');
7
+ /**
8
+ * `ShowcaseConfig.states` as the scanner evaluated it, with everything it
9
+ * cannot use reported. `undefined` when it is no list, or an empty one.
10
+ *
11
+ * Every slot of the list survives, because a state is identified by its
12
+ * position (the URL, the marker and the discovery manifest use it): an
13
+ * unusable entry becomes a state without pseudo states, which the styleguide
14
+ * shows as an empty column, instead of shifting the entries behind it.
15
+ * A state without a usable name is called `State <position>`.
16
+ *
17
+ * `unevaluable` holds the source positions of the entries the evaluator could
18
+ * not read at all. They are missing from `value`, so an empty slot goes back
19
+ * in at each of them, without a message of its own.
20
+ */
21
+ export function readShowcaseStates(value, className, report, unevaluable = new Set()) {
22
+ if (!Array.isArray(value)) {
23
+ report(`${className} declares invalid states ${describeValue(value)}, expected a list of { name, pseudo }. Skipping.`);
24
+ return undefined;
25
+ }
26
+ const entries = [...value];
27
+ // Ascending, so that each position is already the one the source gives it once the earlier gaps are back.
28
+ for (const index of [...unevaluable].sort((a, b) => a - b))
29
+ entries.splice(index, 0, GAP);
30
+ if (entries.length === 0)
31
+ return undefined;
32
+ const firstIndexOfName = new Map();
33
+ return entries.map((entry, index) => {
34
+ const state = typeof entry === 'object' && entry !== null && !Array.isArray(entry) ? entry : undefined;
35
+ const declaredName = state?.['name'];
36
+ const hasName = typeof declaredName === 'string' && declaredName.trim() !== '';
37
+ if (state && !hasName)
38
+ report(`${className} states[${index}] has no name, expected a non-empty string. Calling it "State ${index + 1}".`);
39
+ else if (!state && entry !== GAP)
40
+ report(`${className} states[${index}] is ${describeValue(entry)}, expected { name, pseudo }. Its column stays empty.`);
41
+ const name = hasName ? declaredName : `State ${index + 1}`;
42
+ const first = firstIndexOfName.get(name);
43
+ if (first !== undefined) {
44
+ report(`${className} state "${name}" repeats the name of states[${first}]; only the first one is used. Its column stays empty.`);
45
+ return { name, pseudo: {} };
46
+ }
47
+ firstIndexOfName.set(name, index);
48
+ if (!state)
49
+ return { name, pseudo: {} };
50
+ const pseudo = state['pseudo'];
51
+ // `readPseudoStates` reports what it drops, but an empty object has nothing to drop.
52
+ if (pseudo === undefined || isEmptyObject(pseudo)) {
53
+ report(`${className} state "${name}" declares no pseudo states. Its column stays empty.`);
54
+ return { name, pseudo: {} };
55
+ }
56
+ return { name, pseudo: readPseudoStates(pseudo, `${className} state "${name}"`, report) ?? {} };
57
+ });
58
+ }
@@ -0,0 +1,26 @@
1
+ import ts from 'typescript';
2
+ export interface SourceFileCache {
3
+ /** Pass to `ts.createProgram` together with the previous program. */
4
+ host: ts.CompilerHost;
5
+ /** Drop every cached file the given program no longer contains. */
6
+ prune(program: ts.Program): void;
7
+ }
8
+ /**
9
+ * A compiler host that hands out the same `SourceFile` for a file that has
10
+ * not changed since it was last parsed.
11
+ *
12
+ * `ts.createProgram(…, oldProgram)` only reuses a file from the old program
13
+ * when the host returns the identical object. The default host reads and
14
+ * parses on every call, so without this cache each rebuild re-parses the
15
+ * whole program, Angular's typings included.
16
+ *
17
+ * A file counts as unchanged while its mtime and size are. An edit that keeps
18
+ * the size and lands within the file system's mtime resolution goes unseen;
19
+ * APFS and ext4 record nanoseconds, so in practice that does not happen.
20
+ *
21
+ * Module resolutions are recomputed on every program. Reusing them would
22
+ * miss a file created after an import of it had already failed to resolve,
23
+ * because the importing file itself has not changed.
24
+ */
25
+ export declare function createSourceFileCache(compilerOptions: ts.CompilerOptions): SourceFileCache;
26
+ //# sourceMappingURL=source-file-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-file-cache.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/source-file-cache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,MAAM,WAAW,eAAe;IAC5B,qEAAqE;IACrE,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC;IACtB,mEAAmE;IACnE,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,GAAG,IAAI,CAAC;CACpC;AAOD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,eAAe,EAAE,EAAE,CAAC,eAAe,GAAG,eAAe,CAmC1F"}
@@ -0,0 +1,60 @@
1
+ import { statSync } from 'fs';
2
+ import ts from 'typescript';
3
+ /**
4
+ * A compiler host that hands out the same `SourceFile` for a file that has
5
+ * not changed since it was last parsed.
6
+ *
7
+ * `ts.createProgram(…, oldProgram)` only reuses a file from the old program
8
+ * when the host returns the identical object. The default host reads and
9
+ * parses on every call, so without this cache each rebuild re-parses the
10
+ * whole program, Angular's typings included.
11
+ *
12
+ * A file counts as unchanged while its mtime and size are. An edit that keeps
13
+ * the size and lands within the file system's mtime resolution goes unseen;
14
+ * APFS and ext4 record nanoseconds, so in practice that does not happen.
15
+ *
16
+ * Module resolutions are recomputed on every program. Reusing them would
17
+ * miss a file created after an import of it had already failed to resolve,
18
+ * because the importing file itself has not changed.
19
+ */
20
+ export function createSourceFileCache(compilerOptions) {
21
+ const host = ts.createCompilerHost(compilerOptions);
22
+ const parse = host.getSourceFile.bind(host);
23
+ const cache = new Map();
24
+ host.getSourceFile = (fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile) => {
25
+ const stamp = readStamp(fileName);
26
+ if (stamp === undefined) {
27
+ cache.delete(fileName);
28
+ return undefined;
29
+ }
30
+ const cached = cache.get(fileName);
31
+ if (cached?.stamp === stamp && !shouldCreateNewSourceFile)
32
+ return cached.sourceFile;
33
+ const sourceFile = parse(fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile);
34
+ if (sourceFile)
35
+ cache.set(fileName, { stamp, sourceFile });
36
+ else
37
+ cache.delete(fileName);
38
+ return sourceFile;
39
+ };
40
+ host.hasInvalidatedResolutions = () => true;
41
+ return {
42
+ host,
43
+ prune(program) {
44
+ const live = new Set(program.getSourceFiles().map((sf) => sf.fileName));
45
+ for (const fileName of cache.keys()) {
46
+ if (!live.has(fileName))
47
+ cache.delete(fileName);
48
+ }
49
+ }
50
+ };
51
+ }
52
+ function readStamp(fileName) {
53
+ try {
54
+ const { mtimeMs, size } = statSync(fileName);
55
+ return `${mtimeMs}:${size}`;
56
+ }
57
+ catch {
58
+ return undefined;
59
+ }
60
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"prism-pipeline.d.ts","sourceRoot":"","sources":["../../../src/builder/shared/prism-pipeline.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAShE,OAAO,EAAiB,KAAK,OAAO,EAAmB,MAAM,uBAAuB,CAAC;AAErF,MAAM,WAAW,oBAAoB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,mGAAmG;IACnG,OAAO,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,mGAAmG;IACnG,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,wBAAgB,mBAAmB,IAAI,kBAAkB,CAExD;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuFtJ"}
1
+ {"version":3,"file":"prism-pipeline.d.ts","sourceRoot":"","sources":["../../../src/builder/shared/prism-pipeline.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAUhE,OAAO,EAAiB,KAAK,OAAO,EAAmB,MAAM,uBAAuB,CAAC;AAErF,MAAM,WAAW,oBAAoB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,mGAAmG;IACnG,OAAO,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,mGAAmG;IACnG,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,wBAAgB,mBAAmB,IAAI,kBAAkB,CAExD;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA2FtJ"}
@@ -3,9 +3,10 @@ import { dirname, join } from 'path';
3
3
  import ts from 'typescript';
4
4
  import { checkA11yThresholds, DEFAULT_A11Y_REPORT_PATH, readA11yForComponents, readA11yMeta } from '../a11y/a11y-report-reader.js';
5
5
  import { loadConfig } from '../config-loader/config-loader.js';
6
+ import { describeDeprecatedOptions } from '../config-loader/deprecated-options.js';
6
7
  import { generateRuntimeManifest } from '../manifest/runtime-manifest.generator.js';
7
8
  import { runPluginHooks } from '../plugin-runner/plugin-runner.js';
8
- import { discoverSecondaryEntryPoints } from '../scanner/entry-point-discovery.js';
9
+ import { discoverSecondaryEntryPoints, findPrimaryEntryFile } from '../scanner/entry-point-discovery.js';
9
10
  import { createScanner } from '../scanner/scanner.js';
10
11
  export function createPipelineState() {
11
12
  return { scanner: undefined, lastEntrySetKey: undefined };
@@ -17,6 +18,9 @@ export async function runPrismPipeline(options, context, state) {
17
18
  workspaceRoot,
18
19
  configFileName: options.configFile
19
20
  });
21
+ for (const notice of describeDeprecatedOptions(config, options.configFile)) {
22
+ console.warn(`⚠ ng-prism: ${notice}`);
23
+ }
20
24
  context.reportStatus('Scanning components...');
21
25
  const { components, diagnostics } = scanEntryPoints(workspaceRoot, options, state);
22
26
  if (config.strictShowcase && diagnostics.length > 0) {
@@ -115,6 +119,18 @@ function findLibraryRoot(filePath) {
115
119
  }
116
120
  return undefined;
117
121
  }
122
+ /**
123
+ * Reads config files, `extends` included, but lists no directories. Only
124
+ * `paths` and `baseUrl` are taken from the config, so enumerating the files it
125
+ * includes would only cost time: with no `include`, that is every source file
126
+ * below the config, on every rebuild.
127
+ */
128
+ const CONFIG_HOST = {
129
+ useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
130
+ fileExists: ts.sys.fileExists,
131
+ readFile: ts.sys.readFile,
132
+ readDirectory: () => []
133
+ };
118
134
  function resolveTsconfigPaths(entryPointDir) {
119
135
  const configPath = ts.findConfigFile(entryPointDir, ts.sys.fileExists, 'tsconfig.json');
120
136
  if (!configPath)
@@ -122,7 +138,7 @@ function resolveTsconfigPaths(entryPointDir) {
122
138
  const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
123
139
  if (configFile.error)
124
140
  return {};
125
- const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, dirname(configPath));
141
+ const parsed = ts.parseJsonConfigFileContent(configFile.config, CONFIG_HOST, dirname(configPath));
126
142
  const result = {};
127
143
  if (parsed.options.paths)
128
144
  result.paths = parsed.options.paths;
@@ -136,18 +152,17 @@ function resolveEntryPoints(workspaceRoot, options) {
136
152
  const entryIsDirectory = isDirectory(absoluteEntryPoint);
137
153
  const libraryRoot = entryIsDirectory ? absoluteEntryPoint : findLibraryRoot(absoluteEntryPoint);
138
154
  const discovered = libraryRoot ? discoverSecondaryEntryPoints(libraryRoot, options.libraryImportPath) : [];
139
- // Fallback to a direct file scan when:
140
- // - the entry is a file and no ng-package.json was found above it, OR
141
- // - the entry is a file with an ng-package.json above it but discovery found nothing
142
- // (e.g. primary-only library, root ng-package.json carries `dest`).
143
- const entryPoints = discovered.length === 0 && !entryIsDirectory
144
- ? [
145
- {
146
- entryFile: absoluteEntryPoint,
147
- importPath: options.libraryImportPath
148
- }
149
- ]
155
+ // The primary entry point is scanned after the secondary ones: a file entryPoint is
156
+ // the primary itself, a directory names it in its ng-package.json. A root
157
+ // ng-package.json without `dest` is already among the discovered ones.
158
+ const primaryEntryFile = entryIsDirectory ? findPrimaryEntryFile(absoluteEntryPoint) : absoluteEntryPoint;
159
+ const entryPoints = primaryEntryFile && !discovered.some((e) => e.entryFile === primaryEntryFile)
160
+ ? [...discovered, { entryFile: primaryEntryFile, importPath: options.libraryImportPath, primary: true }]
150
161
  : discovered;
162
+ if (entryPoints.length === 0) {
163
+ console.warn(`⚠ ng-prism: entryPoint "${options.entryPoint}" resolves to no entry file: no ng-package.json with a lib.entryFile ` +
164
+ `that exists, and no secondary entry points below it. Point entryPoint at the library's barrel file.`);
165
+ }
151
166
  return { entryPoints, compilerOptions };
152
167
  }
153
168
  function scanEntryPoints(workspaceRoot, options, state) {