Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@bennypowers
Copy link
Collaborator

Fixes #3999

@bennypowers bennypowers requested a review from kevinpschaaf as a code owner July 9, 2023 12:15
@changeset-bot
Copy link

changeset-bot bot commented Jul 9, 2023

🦋 Changeset detected

Latest commit: 04dbe7a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@lit-labs/analyzer Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@bennypowers bennypowers marked this pull request as draft July 9, 2023 13:04
@bennypowers
Copy link
Collaborator Author

parseJsonConfigFileContent seems to be resolving the includes/excludes/files just fine, but does not pick up on the project references or *.css declaration file, and it in fact looks like it's including everything in the pfe monorepo. Could be i'm holding it wrong, but other ts tooling has been working ok.

const existingOptions: CompilerOptions = {};
if (options.exclude !== undefined) {
(configFile.config.exclude ??= []).push(...options.exclude);
existingOptions.exclude = options.exclude;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know for sure that this doesn't override the excludes in the config file? Maybe you should still concat the two?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't know for sure, but internally, that param is called existingOptions, and it is processed in https://github.com/microsoft/TypeScript/blob/9701f55f7217d7bd56d28e73b250444efcefa8dd/src/compiler/commandLineParser.ts#L2859

packagePath /* basePath */,
undefined /* existingOptions */,
path.relative(packagePath, configFileName) /* configFileName */
ts.sys,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to keep the comments I think 👀

@bennypowers
Copy link
Collaborator Author

as of a82b0e7, the analyzer picks up on my source files, but it does not seem to parse the tsconfig correctly. namely, I get a bunch of errors which i don't normally get with typescript, probably related to build mode

errors

 core/pfe-core/controllers/cascade-controller.ts
  TS6059 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/functions/debounce.ts' is not under 'rootDir' '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. 'rootDir' is expected to contain all source files.

 core/pfe-core/controllers/cascade-controller.ts
  TS6307 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/functions/debounce.ts' is not listed within the file list of project '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. Projects must list all files or use an 'include' pattern.

 core/pfe-core/controllers/internals-controller.ts
  TS2339 Property 'role' does not exist on type 'ARIAMixin'.

 core/pfe-core/controllers/internals-controller.ts
  TS2339 Property 'ariaInvalid' does not exist on type 'ARIAMixin'.

 core/pfe-core/controllers/internals-controller.ts
  TS2339 Property 'validity' does not exist on type 'ElementInternals'.

 core/pfe-core/controllers/internals-controller.ts
  TS2339 Property 'setValidity' does not exist on type 'ElementInternals'.

 core/pfe-core/controllers/internals-controller.ts
  TS2339 Property 'setValidity' does not exist on type 'ElementInternals'.

 core/pfe-core/controllers/internals-controller.ts
  TS2339 Property 'checkValidity' does not exist on type 'ElementInternals'.

 core/pfe-core/controllers/internals-controller.ts
  TS2339 Property 'checkValidity' does not exist on type 'ElementInternals'.

 core/pfe-core/controllers/internals-controller.ts
  TS2339 Property 'reportValidity' does not exist on type 'ElementInternals'.

 core/pfe-core/controllers/internals-controller.ts
  TS2339 Property 'reportValidity' does not exist on type 'ElementInternals'.

 core/pfe-core/controllers/overflow-controller.ts
  TS6059 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/functions/isElementInView.ts' is not under 'rootDir' '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. 'rootDir' is expected to contain all source files.

 core/pfe-core/controllers/overflow-controller.ts
  TS6307 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/functions/isElementInView.ts' is not listed within the file list of project '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. Projects must list all files or use an 'include' pattern.

 core/pfe-core/decorators.ts
  TS6059 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/decorators/bound.ts' is not under 'rootDir' '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. 'rootDir' is expected to contain all source files.

 core/pfe-core/decorators.ts
  TS6307 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/decorators/bound.ts' is not listed within the file list of project '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. Projects must list all files or use an 'include' pattern.

 core/pfe-core/decorators.ts
  TS6059 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/decorators/cascades.ts' is not under 'rootDir' '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. 'rootDir' is expected to contain all source files.

 core/pfe-core/decorators.ts
  TS6307 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/decorators/cascades.ts' is not listed within the file list of project '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. Projects must list all files or use an 'include' pattern.

 core/pfe-core/decorators.ts
  TS6059 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/decorators/deprecation.ts' is not under 'rootDir' '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. 'rootDir' is expected to contain all source files.

 core/pfe-core/decorators.ts
  TS6307 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/decorators/deprecation.ts' is not listed within the file list of project '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. Projects must list all files or use an 'include' pattern.

 core/pfe-core/decorators.ts
  TS6059 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/decorators/initializer.ts' is not under 'rootDir' '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. 'rootDir' is expected to contain all source files.

 core/pfe-core/decorators.ts
  TS6307 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/decorators/initializer.ts' is not listed within the file list of project '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. Projects must list all files or use an 'include' pattern.

 core/pfe-core/decorators.ts
  TS6059 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/decorators/observed.ts' is not under 'rootDir' '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. 'rootDir' is expected to contain all source files.

 core/pfe-core/decorators.ts
  TS6307 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/decorators/observed.ts' is not listed within the file list of project '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. Projects must list all files or use an 'include' pattern.

 core/pfe-core/decorators.ts
  TS6059 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/decorators/time.ts' is not under 'rootDir' '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. 'rootDir' is expected to contain all source files.

 core/pfe-core/decorators.ts
  TS6307 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/decorators/time.ts' is not listed within the file list of project '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. Projects must list all files or use an 'include' pattern.

 core/pfe-core/decorators.ts
  TS6059 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/decorators/trace.ts' is not under 'rootDir' '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. 'rootDir' is expected to contain all source files.

 core/pfe-core/decorators.ts
  TS6307 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/decorators/trace.ts' is not listed within the file list of project '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. Projects must list all files or use an 'include' pattern.

 core/pfe-core/decorators/cascades.ts
  TS6059 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/controllers/cascade-controller.ts' is not under 'rootDir' '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. 'rootDir' is expected to contain all source files.

 core/pfe-core/decorators/cascades.ts
  TS6307 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/controllers/cascade-controller.ts' is not listed within the file list of project '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. Projects must list all files or use an 'include' pattern.

 core/pfe-core/decorators/initializer.ts
  TS6059 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/controllers/light-dom-controller.ts' is not under 'rootDir' '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. 'rootDir' is expected to contain all source files.

 core/pfe-core/decorators/initializer.ts
  TS6307 File '/home/bennyp/Developer/patternfly/patternfly-elements/core/pfe-core/controllers/light-dom-controller.ts' is not listed within the file list of project '/home/bennyp/Developer/patternfly/patternfly-elements/elements/tsconfig.json'. Projects must list all 

this change reduced the number of errors when analyzing patternfly-elements from 152 to 79
@bennypowers bennypowers marked this pull request as ready for review July 10, 2023 09:16
@bennypowers
Copy link
Collaborator Author

bennypowers commented Jul 10, 2023

By tweaking my monorepo project's tsconfigs i reduced the number of errors to 17, all of which appear to be lib problems, presumably because analyzer uses typescript 4.7.4 whereas I'm up to 5.1.6 by now

core/pfe-core/controllers/internals-controller.ts:8:27 - error TS2339: Property 'role' does not exist on type 'ARIAMixin'.

8   declare role: ARIAMixin['role'];
                            ~~~~~~
rest of the errors
core/pfe-core/controllers/internals-controller.ts:22:34 - error TS2339: Property 'ariaInvalid' does not exist on type 'ARIAMixin'.

22   declare ariaInvalid: ARIAMixin['ariaInvalid'];
                                    ~~~~~~~~~~~~~
core/pfe-core/controllers/internals-controller.ts:65:28 - error TS2339: Property 'validity' does not exist on type 'ElementInternals'.

65     return this.#internals.validity;
                              ~~~~~~~~
core/pfe-core/controllers/internals-controller.ts:108:52 - error TS2339: Property 'setValidity' does not exist on type 'ElementInternals'.

108   setValidity(...args: Parameters<ElementInternals['setValidity']>) {
                                                       ~~~~~~~~~~~~~
core/pfe-core/controllers/internals-controller.ts:109:28 - error TS2339: Property 'setValidity' does not exist on type 'ElementInternals'.

109     return this.#internals.setValidity(...args);
                               ~~~~~~~~~~~
core/pfe-core/controllers/internals-controller.ts:112:54 - error TS2339: Property 'checkValidity' does not exist on type 'ElementInternals'.

112   checkValidity(...args: Parameters<ElementInternals['checkValidity']>) {
                                                         ~~~~~~~~~~~~~~~
core/pfe-core/controllers/internals-controller.ts:113:28 - error TS2339: Property 'checkValidity' does not exist on type 'ElementInternals'.

113     return this.#internals.checkValidity(...args);
                               ~~~~~~~~~~~~~
core/pfe-core/controllers/internals-controller.ts:116:55 - error TS2339: Property 'reportValidity' does not exist on type 'ElementInternals'.

116   reportValidity(...args: Parameters<ElementInternals['reportValidity']>) {
                                                          ~~~~~~~~~~~~~~~~
core/pfe-core/controllers/internals-controller.ts:117:28 - error TS2339: Property 'reportValidity' does not exist on type 'ElementInternals'.

117     return this.#internals.reportValidity(...args);
                               ~~~~~~~~~~~~~~
elements/pf-jump-links/pf-jump-links-item.ts:35:5 - error TS2345: Argument of type '{ role: string; }' is not assignable to parameter of type 'Partial<ARIAMixin>'.
  Object literal may only specify known properties, and 'role' does not exist in type 'Partial<ARIAMixin>'.

35     role: 'listitem'
       ~~~~~~~~~~~~~~~~
elements/pf-popover/pf-popover.ts:193:5 - error TS2304: Cannot find name 'satisfies'.

193   } satisfies Record<AlertSeverity, string>) as [AlertSeverity, string][]);
        ~~~~~~~~~
elements/pf-popover/pf-popover.ts:193:5 - error TS2554: Expected 1 arguments, but got 3.

193   } satisfies Record<AlertSeverity, string>) as [AlertSeverity, string][]);
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
elements/pf-popover/pf-popover.ts:193:15 - error TS2693: 'Record' only refers to a type, but is being used as a value here.

193   } satisfies Record<AlertSeverity, string>) as [AlertSeverity, string][]);
                  ~~~~~~
elements/pf-progress-stepper/pf-progress-step.ts:55:5 - error TS2345: Argument of type '{ role: string; }' is not assignable to parameter of type 'Partial<ARIAMixin>'.
  Object literal may only specify known properties, and 'role' does not exist in type 'Partial<ARIAMixin>'.

55     role: 'listitem',
       ~~~~~~~~~~~~~~~~
elements/pf-progress-stepper/pf-progress-stepper.ts:38:5 - error TS2345: Argument of type '{ role: string; ariaValueNow: string; }' is not assignable to parameter of type 'Partial<ARIAMixin>'.
  Object literal may only specify known properties, and 'role' does not exist in type 'Partial<ARIAMixin>'.

38     role: 'progressbar',
       ~~~~~~~~~~~~~~~~~~~
elements/pf-tabs/BaseTab.ts:43:21 - error TS2339: Property 'role' does not exist on type 'ElementInternals'.

43     this.#internals.role = 'tab';
                       ~~~~
elements/pf-tabs/BaseTabPanel.ts:22:21 - error TS2339: Property 'role' does not exist on type 'ElementInternals'.

22     this.#internals.role = 'tabpanel';
                       ~~~~

17 errors

So with that in mind i think this is ready for review

@justinfagnani
Copy link
Collaborator

Tagging in @kevinpschaaf and @rictic both for the review, but also... we need to think about supporting multiple versions of TypeScript.

@bennypowers
Copy link
Collaborator Author

Just want to record here that declaration augmenting fixed the above errors, but I'm left with this, despite the corresponding declaration being in scope :

elements/pf-accordion/BaseAccordion.ts:14:19 - error TS2323: Could not resolve specifier ./BaseAccordion.css to filesystem path.

14 import style from './BaseAccordion.css';
                     ~~~~~~~~~~~~~~~~~~~~~

1 errors

@justinfagnani
Copy link
Collaborator

I think that is because the analyzer is actually trying to find that file on disk. It should really give up if it can't.

@43081j
Copy link
Collaborator

43081j commented Jul 11, 2023

im still curious/mildly concerned of how the error got code: 2323

the analyzer explicitly passes a special lit error code, not 2323. would be worth us finding out if that got there just by how benny was logging stuff, or its the analyzer's fault.

could lead to confusion in future if other analyzer errors accidentally end up with typescript error codes...

here we don't specify a code so it should be the default UNKNOWN one (548000). so i expected error TS548000, not error TS2323

@bennypowers
Copy link
Collaborator Author

bennypowers commented Jul 11, 2023

here's my calling code:

async function analyze(argv: Opts) {
  const { packagePath } = argv;
  const path = packagePath instanceof URL ? fileURLToPath(packagePath) : join(process.cwd(), packagePath);
  const analyzer = createPackageAnalyzer(path as AbsolutePath, {
    exclude: [
      '**/*.spec.ts',
      '**/*.e2e.ts',
      '**/*.d.ts',
      '**/*.js',
    ],
  });
  try {
    const p = analyzer.getPackage();
    console.log('got package', p);
  } catch (e: any) {
    if (Array.isArray(e.diagnostics)) {
      console.log(e.diagnostics.at(0));
      const formattedDiagnostics = formatDiagnostics(e.diagnostics);
      console.log(`${formattedDiagnostics}\n${e.diagnostics.length} errors`);
    } else {
      console.group(`Analyzer error`);
      console.error(e);
      console.groupEnd();
    }
  }
}

here is the diagnostic object from e.diagnostic, redacted for readability, where you can see that the code i get from analyzer is 2323:

{
  file: <ref *1> SourceFileObject { ... },
  start: 566,
  length: 21,
  category: 1,
  code: 2323,
  messageText: 'Could not resolve specifier ./BaseAccordion.css to filesystem path.'
}
entire diagnostic object
{
  file: <ref *1> SourceFileObject {
    pos: 0,
    end: 11995,
    flags: 2048,
    modifierFlagsCache: 0,
    transformFlags: 10618289,
    parent: undefined,
    kind: 305,
    statements: [
      [NodeObject],
      [NodeObject],
      [NodeObject],
      [NodeObject],
      [NodeObject],
      [NodeObject],
      [NodeObject],
      [NodeObject],
      [NodeObject],
      [NodeObject],
      [NodeObject],
      [NodeObject],
      [NodeObject],
      pos: 0,
      end: 11994,
      hasTrailingComma: false,
      transformFlags: 10618289
    ],
    endOfFileToken: TokenObject {
      pos: 11994,
      end: 11995,
      flags: 0,
      modifierFlagsCache: 0,
      transformFlags: 0,
      parent: [Circular *1],
      kind: 1
    },
    fileName: '/home/bennyp/Developer/patternfly/patternfly-elements/elements/pf-accordion/BaseAccordion.ts',
    text: "import type { TemplateResult } from 'lit';\n" +
      '\n' +
      "import { LitElement, html } from 'lit';\n" +
      "import { property } from 'lit/decorators/property.js';\n" +
      '\n' +
      "import { NumberListConverter, ComposedEvent } from '@patternfly/pfe-core';\n" +
      "import { Logger } from '@patternfly/pfe-core/controllers/logger.js';\n" +
      '\n' +
      "import { AccordionHeaderChangeEvent, BaseAccordionHeader } from './BaseAccordionHeader.js';\n" +
      "import { BaseAccordionPanel } from './BaseAccordionPanel.js';\n" +
      '\n' +
      "import { RovingTabindexController } from '@patternfly/pfe-core/controllers/roving-tabindex-controller.js';\n" +
      '\n' +
      "import style from './BaseAccordion.css';\n" +
      '\n' +
      'const CSS_TIMING_UNITS_RE = /^[0-9.]+(?<unit>[a-zA-Z]+)/g;\n' +
      '\n' +
      'export class AccordionExpandEvent extends ComposedEvent {\n' +
      '  constructor(\n' +
      '    public toggle: BaseAccordionHeader,\n' +
      '    public panel: BaseAccordionPanel,\n' +
      '  ) {\n' +
      "    super('expand');\n" +
      '  }\n' +
      '}\n' +
      '\n' +
      'export class AccordionCollapseEvent extends ComposedEvent {\n' +
      '  constructor(\n' +
      '    public toggle: BaseAccordionHeader,\n' +
      '    public panel: BaseAccordionPanel,\n' +
      '  ) {\n' +
      "    super('collapse');\n" +
      '  }\n' +
      '}\n' +
      '\n' +
      'export abstract class BaseAccordion extends LitElement {\n' +
      '  static readonly styles = [style];\n' +
      '\n' +
      '  static isAccordion(target: EventTarget | null): target is BaseAccordion {\n' +
      '    return target instanceof BaseAccordion;\n' +
      '  }\n' +
      '\n' +
      '  static isHeader(target: EventTarget | null): target is BaseAccordionHeader {\n' +
      '    return target instanceof BaseAccordionHeader;\n' +
      '  }\n' +
      '\n' +
      '  static isPanel(target: EventTarget | null): target is BaseAccordionPanel {\n' +
      '    return target instanceof BaseAccordionPanel;\n' +
      '  }\n' +
      '\n' +
      '  #headerIndex = new RovingTabindexController<BaseAccordionHeader>(this);\n' +
      '\n' +
      '  #expandedIndex: number[] = [];\n' +
      '\n' +
      '  /**\n' +
      '   * Sets and reflects the currently expanded accordion 0-based indexes.\n' +
      '   * Use commas to separate multiple indexes.\n' +
      '   * ```html\n' +
      '   * <pf-accordion expanded-index="1,2">\n' +
      '   *   ...\n' +
      '   * </pf-accordion>\n' +
      '   * ```\n' +
      '   */\n' +
      '  @property({\n' +
      "    attribute: 'expanded-index',\n" +
      '    converter: NumberListConverter\n' +
      '  })\n' +
      '  get expandedIndex() {\n' +
      '    return this.#expandedIndex;\n' +
      '  }\n' +
      '\n' +
      '  set expandedIndex(value) {\n' +
      '    const old = this.#expandedIndex;\n' +
      '    this.#expandedIndex = value;\n' +
      '    if (JSON.stringify(old) !== JSON.stringify(value)) {\n' +
      "      this.requestUpdate('expandedIndex', old);\n" +
      '      this.collapseAll().then(async () => {\n' +
      '        for (const i of this.expandedIndex) {\n' +
      '          await this.expand(i, this);\n' +
      '        }\n' +
      '      });\n' +
      '    }\n' +
      '  }\n' +
      '\n' +
      '  get headers() {\n' +
      '    return this.#allHeaders();\n' +
      '  }\n' +
      '\n' +
      '  get panels() {\n' +
      '    return this.#allPanels();\n' +
      '  }\n' +
      '\n' +
      '  get #activeHeader() {\n' +
      '    const { headers } = this;\n' +
      "    const index = headers.findIndex(header => header.matches(':focus,:focus-within'));\n" +
      '    return headers.at(index);\n' +
      '  }\n' +
      '\n' +
      '  protected expandedSets = new Set<number>();\n' +
      '\n' +
      '  #logger = new Logger(this);\n' +
      '\n' +
      '  #styles = getComputedStyle(this);\n' +
      '\n' +
      '  #transitionDuration = this.#getAnimationDuration();\n' +
      '\n' +
      '  // actually is read in #init, by the `||=` operator\n' +
      '  #initialized = false;\n' +
      '\n' +
      '  protected override async getUpdateComplete(): Promise<boolean> {\n' +
      '    const c = await super.getUpdateComplete();\n' +
      '    const results = await Promise.all([\n' +
      '      ...this.#allHeaders().map(x => x.updateComplete),\n' +
      '      ...this.#allPanels().map(x => x.updateComplete),\n' +
      '    ]);\n' +
      '    return c && results.every(Boolean);\n' +
      '  }\n' +
      '\n' +
      '  #mo = new MutationObserver(() => this.#init());\n' +
      '\n' +
      '  connectedCallback() {\n' +
      '    super.connectedCallback();\n' +
      "    this.addEventListener('change', this.#onChange as EventListener);\n" +
      '    this.#mo.observe(this, { childList: true });\n' +
      '    this.#init();\n' +
      '  }\n' +
      '\n' +
      '  render(): TemplateResult {\n' +
      '    return html`\n' +
      '      <slot></slot>\n' +
      '    `;\n' +
      '  }\n' +
      '\n' +
      '  async firstUpdated() {\n' +
      '    const { headers } = this;\n' +
      '    headers.forEach((header, index) => {\n' +
      '      if (header.expanded) {\n' +
      '        this.#expandHeader(header, index);\n' +
      '        const panel = this.#panelForHeader(header);\n' +
      '        if (panel) {\n' +
      '          this.#expandPanel(panel);\n' +
      '        }\n' +
      '      }\n' +
      '    });\n' +
      '  }\n' +
      '\n' +
      '  /**\n' +
      '   * Initialize the accordion by connecting headers and panels\n' +
      '   * with aria controls and labels; set up the default disclosure\n' +
      '   * state if not set by the author; and check the URL for default\n' +
      '   * open\n' +
      '   */\n' +
      '  async #init() {\n' +
      '    this.#initialized ||= !!await this.updateComplete;\n' +
      '    this.#headerIndex.initItems(this.headers);\n' +
      '    // Event listener to the accordion header after the accordion has been initialized to add the roving tabindex\n' +
      "    this.addEventListener('focusin', this.#updateActiveHeader as EventListener);\n" +
      '    this.updateAccessibility();\n' +
      '  }\n' +
      '\n' +
      '  #updateActiveHeader() {\n' +
      '    if (this.#activeHeader) {\n' +
      '      this.#headerIndex.updateActiveItem(this.#activeHeader);\n' +
      '    }\n' +
      '  }\n' +
      '\n' +
      '  #panelForHeader(header: BaseAccordionHeader) {\n' +
      '    const next = header.nextElementSibling;\n' +
      '    if (!BaseAccordion.isPanel(next)) {\n' +
      "      return void this.#logger.error('Sibling element to a header needs to be a panel');\n" +
      '    } else {\n' +
      '      return next;\n' +
      '    }\n' +
      '  }\n' +
      '\n' +
      '  #expandHeader(header: BaseAccordionHeader, index = this.#getIndex(header)) {\n' +
      '    // If this index is not already listed in the expandedSets array, add it\n' +
      '    this.expandedSets.add(index);\n' +
      '    this.#expandedIndex = [...this.expandedSets as Set<number>];\n' +
      '    header.expanded = true;\n' +
      '  }\n' +
      '\n' +
      '  async #expandPanel(panel: BaseAccordionPanel) {\n' +
      '    panel.expanded = true;\n' +
      '    panel.hidden = false;\n' +
      '\n' +
      '    await panel.updateComplete;\n' +
      '\n' +
      '    const rect = panel.getBoundingClientRect();\n' +
      '\n' +
      '    this.#animate(panel, 0, rect.height);\n' +
      '  }\n' +
      '\n' +
      '  async #collapseHeader(header: BaseAccordionHeader, index = this.#getIndex(header)) {\n' +
      '    if (!this.expandedSets) {\n' +
      '      await this.updateComplete;\n' +
      '    }\n' +
      '    this.expandedSets.delete(index);\n' +
      '    header.expanded = false;\n' +
      '    await header.updateComplete;\n' +
      '  }\n' +
      '\n' +
      '  async #collapsePanel(panel: BaseAccordionPanel) {\n' +
      '    await panel.updateComplete;\n' +
      '    if (!panel.expanded) {\n' +
      '      return;\n' +
      '    }\n' +
      '\n' +
      '    const rect = panel.getBoundingClientRect();\n' +
      '\n' +
      '    panel.expanded = false;\n' +
      '    panel.hidden = true;\n' +
      '\n' +
      '    this.#animate(panel, rect.height, 0);\n' +
      '    await panel.updateComplete;\n' +
      '  }\n' +
      '\n' +
      '  #getAnimationDuration(): number {\n' +
      "    if ('computedStyleMap' in (this as HTMLElement)) {\n" +
      '      // @ts-expect-error: https://caniuse.com/?search=computedStyleMap\n' +
      "      return this.computedStyleMap().get('transition-duration')?.to('ms').value;\n" +
      '    } else {\n' +
      '      const { transitionDuration } = this.#styles;\n' +
      '\n' +
      '      const groups = CSS_TIMING_UNITS_RE.exec(transitionDuration)?.groups;\n' +
      '\n' +
      '      if (!groups) {\n' +
      '        return 0;\n' +
      '      }\n' +
      '\n' +
      '      const parsed = parseFloat(transitionDuration);\n' +
      '\n' +
      "      if (groups.unit === 's') {\n" +
      '        return parsed * 1000;\n' +
      '      } else {\n' +
      '        return parsed;\n' +
      '      }\n' +
      '    }\n' +
      '  }\n' +
      '\n' +
      '  async #animate(panel: BaseAccordionPanel, start: number, end: number) {\n' +
      '    if (panel) {\n' +
      '      const header = panel.previousElementSibling;\n' +
      '\n' +
      '      const transitionDuration = this.#getAnimationDuration();\n' +
      '      if (transitionDuration) {\n' +
      '        this.#transitionDuration = transitionDuration;\n' +
      '      }\n' +
      '\n' +
      '      const duration = this.#transitionDuration ?? 0;\n' +
      '\n' +
      "      header?.classList.add('animating');\n" +
      "      panel.classList.add('animating');\n" +
      '\n' +
      '      const animation = panel.animate({ height: [`${start}px`, `${end}px`] }, { duration });\n' +
      '      animation.play();\n' +
      '      await animation.finished;\n' +
      '\n' +
      "      header?.classList.remove('animating');\n" +
      "      panel.classList.remove('animating');\n" +
      '\n' +
      "      panel.style.removeProperty('height');\n" +
      '      panel.hidden = !panel.expanded;\n' +
      '    }\n' +
      '  }\n' +
      '\n' +
      '  #onChange(event: AccordionHeaderChangeEvent) {\n' +
      "    if (this.classList.contains('animating')) {\n" +
      '      return;\n' +
      '    }\n' +
      '\n' +
      '    const index = this.#getIndex(event.target as Element);\n' +
      '\n' +
      '    if (event.expanded) {\n' +
      '      this.expand(index, event.accordion);\n' +
      '    } else {\n' +
      '      this.collapse(index);\n' +
      '    }\n' +
      '  }\n' +
      '\n' +
      '  /**\n' +
      '   * @see https://www.w3.org/TR/wai-aria-practices/#accordion\n' +
      '   */\n' +
      '  async #onKeydown(evt: KeyboardEvent) {\n' +
      '    const currentHeader = evt.target as Element;\n' +
      '\n' +
      '    if (!BaseAccordion.isHeader(currentHeader)) {\n' +
      '      return;\n' +
      '    }\n' +
      '\n' +
      '    let newHeader: BaseAccordionHeader | undefined;\n' +
      '\n' +
      '    switch (evt.key) {\n' +
      "      case 'ArrowDown':\n" +
      '        evt.preventDefault();\n' +
      '        newHeader = this.#nextHeader();\n' +
      '        break;\n' +
      "      case 'ArrowUp':\n" +
      '        evt.preventDefault();\n' +
      '        newHeader = this.#previousHeader();\n' +
      '        break;\n' +
      "      case 'Home':\n" +
      '        evt.preventDefault();\n' +
      '        newHeader = this.#firstHeader();\n' +
      '        break;\n' +
      "      case 'End':\n" +
      '        evt.preventDefault();\n' +
      '        newHeader = this.#lastHeader();\n' +
      '        break;\n' +
      '    }\n' +
      '\n' +
      '    newHeader?.focus?.();\n' +
      '  }\n' +
      '\n' +
      '  #allHeaders(accordion: BaseAccordion = this): BaseAccordionHeader[] {\n' +
      '    return Array.from(accordion.children).filter(BaseAccordion.isHeader);\n' +
      '  }\n' +
      '\n' +
      '  #allPanels(accordion: BaseAccordion = this): BaseAccordionPanel[] {\n' +
      '    return Array.from(accordion.children).filter(BaseAccordion.isPanel);\n' +
      '  }\n' +
      '\n' +
      '  #previousHeader() {\n' +
      '    const { headers } = this;\n' +
      "    const newIndex = headers.findIndex(header => header.matches(':focus,:focus-within')) - 1;\n" +
      '    return headers[(newIndex + headers.length) % headers.length];\n' +
      '  }\n' +
      '\n' +
      '  #nextHeader() {\n' +
      '    const { headers } = this;\n' +
      "    const newIndex = headers.findIndex(header => header.matches(':focus,:focus-within')) + 1;\n" +
      '    return headers[newIndex % headers.length];\n' +
      '  }\n' +
      '\n' +
      '  #firstHeader() {\n' +
      '    return this.headers.at(0);\n' +
      '  }\n' +
      '\n' +
      '  #lastHeader() {\n' +
      '    return this.headers.at(-1);\n' +
      '  }\n' +
      '\n' +
      '  #getIndex(el: Element | null) {\n' +
      '    if (BaseAccordion.isHeader(el)) {\n' +
      '      return this.headers.findIndex(header => header.id === el.id);\n' +
      '    }\n' +
      '\n' +
      '    if (BaseAccordion.isPanel(el)) {\n' +
      '      return this.panels.findIndex(panel => panel.id === el.id);\n' +
      '    }\n' +
      '\n' +
      "    this.#logger.warn('The #getIndex method expects to receive a header or panel element.');\n" +
      '    return -1;\n' +
      '  }\n' +
      '\n' +
      '  public updateAccessibility() {\n' +
      '    const { headers } = this;\n' +
      '\n' +
      '    // For each header in the accordion, attach the aria connections\n' +
      '    headers.forEach(header => {\n' +
      '      const panel = this.#panelForHeader(header);\n' +
      '      if (panel) {\n' +
      "        header.setAttribute('aria-controls', panel.id);\n" +
      "        panel.setAttribute('aria-labelledby', header.id);\n" +
      '        panel.hidden = !panel.expanded;\n' +
      '      }\n' +
      '    });\n' +
      '  }\n' +
      '\n' +
      '  /**\n' +
      '   * Accepts a 0-based index value (integer)'... 1995 more characters,
    languageVersion: 9,
    languageVariant: 0,
    scriptKind: 3,
    isDeclarationFile: false,
    hasNoDefaultLib: false,
    bindDiagnostics: [],
    bindSuggestionDiagnostics: undefined,
    externalModuleIndicator: NodeObject {
      pos: 0,
      end: 42,
      flags: 0,
      modifierFlagsCache: 536870912,
      transformFlags: 1,
      parent: [Circular *1],
      kind: 266,
      decorators: undefined,
      modifiers: undefined,
      symbol: undefined,
      localSymbol: undefined,
      locals: undefined,
      nextContainer: undefined,
      importClause: [NodeObject],
      moduleSpecifier: [TokenObject],
      assertClause: undefined
    },
    setExternalModuleIndicator: [Function: callback],
    pragmas: Map(0) {},
    checkJsDirective: undefined,
    referencedFiles: [],
    typeReferenceDirectives: [],
    libReferenceDirectives: [],
    amdDependencies: [],
    commentDirectives: [ [Object] ],
    nodeCount: 1800,
    identifierCount: 487,
    identifiers: Map(137) {
      'type' => 'type',
      'TemplateResult' => 'TemplateResult',
      'lit' => 'lit',
      'LitElement' => 'LitElement',
      'html' => 'html',
      'property' => 'property',
      'lit/decorators/property.js' => 'lit/decorators/property.js',
      'NumberListConverter' => 'NumberListConverter',
      'ComposedEvent' => 'ComposedEvent',
      '@patternfly/pfe-core' => '@patternfly/pfe-core',
      'Logger' => 'Logger',
      '@patternfly/pfe-core/controllers/logger.js' => '@patternfly/pfe-core/controllers/logger.js',
      'AccordionHeaderChangeEvent' => 'AccordionHeaderChangeEvent',
      'BaseAccordionHeader' => 'BaseAccordionHeader',
      './BaseAccordionHeader.js' => './BaseAccordionHeader.js',
      'BaseAccordionPanel' => 'BaseAccordionPanel',
      './BaseAccordionPanel.js' => './BaseAccordionPanel.js',
      'RovingTabindexController' => 'RovingTabindexController',
      '@patternfly/pfe-core/controllers/roving-tabindex-controller.js' => '@patternfly/pfe-core/controllers/roving-tabindex-controller.js',
      'style' => 'style',
      './BaseAccordion.css' => './BaseAccordion.css',
      'CSS_TIMING_UNITS_RE' => 'CSS_TIMING_UNITS_RE',
      'AccordionExpandEvent' => 'AccordionExpandEvent',
      'toggle' => 'toggle',
      'panel' => 'panel',
      'AccordionCollapseEvent' => 'AccordionCollapseEvent',
      'BaseAccordion' => 'BaseAccordion',
      'styles' => 'styles',
      'isAccordion' => 'isAccordion',
      'target' => 'target',
      'EventTarget' => 'EventTarget',
      'isHeader' => 'isHeader',
      'isPanel' => 'isPanel',
      'attribute' => 'attribute',
      'converter' => 'converter',
      'expandedIndex' => 'expandedIndex',
      'value' => 'value',
      'old' => 'old',
      'JSON' => 'JSON',
      'stringify' => 'stringify',
      'requestUpdate' => 'requestUpdate',
      'collapseAll' => 'collapseAll',
      'then' => 'then',
      'i' => 'i',
      'expand' => 'expand',
      'headers' => 'headers',
      'panels' => 'panels',
      'index' => 'index',
      'findIndex' => 'findIndex',
      'header' => 'header',
      'matches' => 'matches',
      'at' => 'at',
      'expandedSets' => 'expandedSets',
      'Set' => 'Set',
      'getComputedStyle' => 'getComputedStyle',
      'getUpdateComplete' => 'getUpdateComplete',
      'Promise' => 'Promise',
      'c' => 'c',
      'results' => 'results',
      'all' => 'all',
      'map' => 'map',
      'x' => 'x',
      'updateComplete' => 'updateComplete',
      'every' => 'every',
      'Boolean' => 'Boolean',
      'MutationObserver' => 'MutationObserver',
      'connectedCallback' => 'connectedCallback',
      'addEventListener' => 'addEventListener',
      'EventListener' => 'EventListener',
      'observe' => 'observe',
      'childList' => 'childList',
      'render' => 'render',
      'firstUpdated' => 'firstUpdated',
      'forEach' => 'forEach',
      'expanded' => 'expanded',
      'initItems' => 'initItems',
      'updateAccessibility' => 'updateAccessibility',
      'updateActiveItem' => 'updateActiveItem',
      'next' => 'next',
      'nextElementSibling' => 'nextElementSibling',
      'error' => 'error',
      'add' => 'add',
      'hidden' => 'hidden',
      'rect' => 'rect',
      'getBoundingClientRect' => 'getBoundingClientRect',
      'height' => 'height',
      'delete' => 'delete',
      'number' => 'number',
      'HTMLElement' => 'HTMLElement',
      'computedStyleMap' => 'computedStyleMap',
      'get' => 'get',
      'to' => 'to',
      'transitionDuration' => 'transitionDuration',
      'groups' => 'groups',
      'exec' => 'exec',
      'parsed' => 'parsed',
      'parseFloat' => 'parseFloat',
      'unit' => 'unit',
      'start' => 'start',
      'end' => 'end',
      'previousElementSibling' => 'previousElementSibling',
      'duration' => 'duration',
      'classList' => 'classList',
      'animation' => 'animation',
      'animate' => 'animate',
      'play' => 'play',
      'finished' => 'finished',
      'remove' => 'remove',
      'removeProperty' => 'removeProperty',
      'event' => 'event',
      'contains' => 'contains',
      'Element' => 'Element',
      'accordion' => 'accordion',
      'collapse' => 'collapse',
      'evt' => 'evt',
      'KeyboardEvent' => 'KeyboardEvent',
      'currentHeader' => 'currentHeader',
      'newHeader' => 'newHeader',
      'key' => 'key',
      'preventDefault' => 'preventDefault',
      'focus' => 'focus',
      'see' => 'see',
      'https' => 'https',
      'Array' => 'Array',
      'from' => 'from',
      'children' => 'children',
      'filter' => 'filter',
      'newIndex' => 'newIndex',
      'length' => 'length',
      'el' => 'el',
      'id' => 'id',
      'warn' => 'warn',
      'setAttribute' => 'setAttribute',
      'parentAccordion' => 'parentAccordion',
      'allHeaders' => 'allHeaders',
      'dispatchEvent' => 'dispatchEvent',
      'expandAll' => 'expandAll'
    },
    parseDiagnostics: [],
    path: '/home/bennyp/Developer/patternfly/patternfly-elements/elements/pf-accordion/BaseAccordion.ts',
    resolvedPath: '/home/bennyp/Developer/patternfly/patternfly-elements/elements/pf-accordion/BaseAccordion.ts',
    originalFileName: '/home/bennyp/Developer/patternfly/patternfly-elements/elements/pf-accordion/BaseAccordion.ts',
    imports: [
      [TokenObject],
      [TokenObject],
      [TokenObject],
      [TokenObject],
      [TokenObject],
      [TokenObject],
      [TokenObject],
      [TokenObject],
      [TokenObject],
      [TokenObject]
    ],
    moduleAugmentations: [],
    ambientModuleNames: [],
    resolvedModules: {
      get: [Function: get],
      set: [Function: set],
      delete: [Function: delete],
      has: [Function: has],
      forEach: [Function: forEach],
      size: [Function: size]
    },
    symbol: SymbolObject {
      flags: 512,
      escapedName: '"/home/bennyp/Developer/patternfly/patternfly-elements/elements/pf-accordion/BaseAccordion"',
      declarations: [Array],
      exports: [Map],
      valueDeclaration: [Circular *1],
      id: 8694
    },
    locals: Map(16) {
      'TemplateResult' => [SymbolObject],
      'LitElement' => [SymbolObject],
      'html' => [SymbolObject],
      'property' => [SymbolObject],
      'NumberListConverter' => [SymbolObject],
      'ComposedEvent' => [SymbolObject],
      'Logger' => [SymbolObject],
      'AccordionHeaderChangeEvent' => [SymbolObject],
      'BaseAccordionHeader' => [SymbolObject],
      'BaseAccordionPanel' => [SymbolObject],
      'RovingTabindexController' => [SymbolObject],
      'style' => [SymbolObject],
      'CSS_TIMING_UNITS_RE' => [SymbolObject],
      'AccordionExpandEvent' => [SymbolObject],
      'AccordionCollapseEvent' => [SymbolObject],
      'BaseAccordion' => [SymbolObject]
    },
    nextContainer: NodeObject {
      pos: 648,
      end: 833,
      flags: 0,
      modifierFlagsCache: 536870913,
      transformFlags: 5121,
      parent: [Circular *1],
      kind: 257,
      decorators: undefined,
      modifiers: [Array],
      symbol: [SymbolObject],
      localSymbol: [SymbolObject],
      locals: undefined,
      nextContainer: [NodeObject],
      name: [IdentifierObject],
      typeParameters: undefined,
      heritageClauses: [Array],
      members: [Array],
      id: 27555
    },
    endFlowNode: { flags: 16, antecedent: [Object], node: [NodeObject] },
    symbolCount: 172,
    classifiableNames: Set(15) {
      'TemplateResult',
      'LitElement',
      'html',
      'property',
      'NumberListConverter',
      'ComposedEvent',
      'Logger',
      'AccordionHeaderChangeEvent',
      'BaseAccordionHeader',
      'BaseAccordionPanel',
      'RovingTabindexController',
      'style',
      'AccordionExpandEvent',
      'AccordionCollapseEvent',
      'BaseAccordion'
    },
    id: 27547,
    lineMap: [
         0,   43,   44,   84,  139,  140,  215,  284,  285,  377,
       439,  440,  547,  548,  589,  590,  649,  650,  708,  723,
       763,  801,  807,  828,  832,  834,  835,  895,  910,  950,
       988,  994, 1017, 1021, 1023, 1024, 1081, 1117, 1118, 1194,
      1238, 1242, 1243, 1322, 1372, 1376, 1377, 1454, 1503, 1507,
      1508, 1582, 1583, 1616, 1617, 1623, 1696, 1742, 1755, 1796,
      1807, 1828, 1837, 1843, 1857, 1890, 1925, 1930, 1954, 1986,
      1990, 1991, 2020, 2057, 2090, 2147, 2195, 2239, 2285, 2323,
      2333, 2343, 2349, 2353, 2354, 2372, 2403, 2407, 2408, 2425,
      2455, 2459, 2460, 2484, 2514, 2601, 2631, 2635, 2636, 2682,
      ... 352 more items
    ]
  },
  start: 566,
  length: 21,
  category: 1,
  code: 2323,
  messageText: 'Could not resolve specifier ./BaseAccordion.css to filesystem path.'
}

@43081j
Copy link
Collaborator

43081j commented Jul 11, 2023

all good @bennypowers , it turns out Peter already fixed it in #3866! non-issue 🥳

i guess it just hasn't been published yet

path.relative(packagePath, configFileName) /* configFileName */
isDirectory ? packagePath : path.dirname(packagePath) /* basePath */,
{} /* existingOptions */,
configFileName /* configFileName */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, this whole API is really funky, but note early on I had left this comment:

// Note that passing `packageRoot` for `basePath` works, but
// `getOutputFileNames` will fail without passing `configFileName`; once you
// pass that, it looks like paths are relative to the `configFileName`
// location (which is also under `packageRoot`), in which case `basePath`
// shouldn't duplicate `packageRoot`

I believe this empirical finding was why the configFileName argument here was being calculated relative to basePath. But I guess if you're not getting the errors I was having with packagePath show up both in basePath and configFileName and the tests are passing, I probably just misjudged what was going on...?

Copy link
Member

@kevinpschaaf kevinpschaaf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, with these changes I've confirmed I can generate a CEM from MWC[*] source, which besides the monorepo tests, was the other thing I had been testing analyzer against, so while I can't really explain why the comment I linked from the older code would seem to indicate this change would break something, it doesn't seem to.

So LGTM, and if we find out this breaks something we'll just need to add more tests.

*To get a successful run on MWC, I did need to upgrade the TS version the analyzer was using to match what MWC was using (per known issue in #4018), and then skip over a couple of unresolvable symbols in MWC code added since I last tested (needs more investigation, but wasn't the failure mode this type of change would lead to).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[labs/analyzer]: No source files found in package when tsconfig extends

4 participants