From d7fbabd8a54e02b49e50dacb52a67875763e0183 Mon Sep 17 00:00:00 2001 From: Katie Gengler Date: Thu, 6 Aug 2026 18:11:37 -0400 Subject: [PATCH] Update generation of import examples for static functions Previously, all import examples for static functions relied upon there being a legacy mapping for the API in mappings.json. This meant that newly added APIs would not display import examples. Deciding whether to generate an import example now relies on whether the package contains an `@` or if the package is `rsvp` -- the sole non-@ package that displays import examples today. This strikes a balance of restoring, even on slightly older versions, the import examples that should have been there while maintaining no examples on very old (<2.18) versions. Additionally added support for @noimport tag in doc blocks to indicate APIs that do no have imports (aka built-in keywords for use in templates). Also added @exampleimport for doc blocks so that import paths can be overridden. Right now the example import generation expects imports to always be import { name } from 'module'; but there are cases where this may not be true so the ability to override and have them specified in the codebase is preferable. --- app/components/class-field-description.gjs | 40 ++++---- app/components/import-example.gjs | 11 ++- .../class-field-description-test.gjs | 92 +++++++++++++++++++ .../components/import-example-test.gjs | 16 ++++ 4 files changed, 138 insertions(+), 21 deletions(-) diff --git a/app/components/class-field-description.gjs b/app/components/class-field-description.gjs index d99c2b67..436d298f 100644 --- a/app/components/class-field-description.gjs +++ b/app/components/class-field-description.gjs @@ -1,11 +1,8 @@ -import { service } from '@ember/service'; import Component from '@glimmer/component'; import svgJar from 'ember-svg-jar/helpers/svg-jar'; import { LinkTo } from '@ember/routing'; import { array, concat } from '@ember/helper'; import githubLink from 'ember-api-docs/helpers/github-link'; -import and from 'ember-truth-helpers/helpers/and'; -import eq from 'ember-api-docs/helpers/eq'; import ImportExample from 'ember-api-docs/components/import-example'; import MarkdownToHtml from 'ember-cli-showdown/components/markdown-to-html'; @@ -19,6 +16,16 @@ function combineNames(items) { } export default class ClassFieldDescription extends Component { + get displayImportExample() { + return ( + this.args.field.exampleimport !== undefined || + (this.args.field.noimport === undefined && + this.args.field.static === 1 && + this.args.field.itemtype === 'method' && + isImportablePackage(this.args.field.class)) + ); + } + - @service - legacyModuleMappings; +} - get hasImportExample() { - return this.legacyModuleMappings.hasFunctionMapping( - this.args.field.name, - this.args.field.class, - ); - } +function isImportablePackage(packageName) { + /* Broadly define which types of static functions display generated import + examples. This previously relied on `mappings.json` but this meant that newly + added APIs did not display import examples. + + This should strike a balance of displaying import examples for new APIs and + not displaying them before the APIs were importable. + */ + return packageName.startsWith('@') || packageName === 'rsvp'; } diff --git a/app/components/import-example.gjs b/app/components/import-example.gjs index 3a528dc1..93797843 100644 --- a/app/components/import-example.gjs +++ b/app/components/import-example.gjs @@ -2,11 +2,16 @@ import Component from '@glimmer/component'; import MarkdownToHtml from 'ember-cli-showdown/components/markdown-to-html'; export default class ImportExample extends Component { - get markdown() { - let md = `\`\`\`js + if (this.args.exampleimport) { + return `\`\`\`js +${this.args.exampleimport} +\`\`\``; + } + return `\`\`\`js import ${this.args.item} from '${this.args.package}'; \`\`\``; - return md; } + + } diff --git a/tests/integration/components/class-field-description-test.gjs b/tests/integration/components/class-field-description-test.gjs index d5700619..a70a9cbe 100644 --- a/tests/integration/components/class-field-description-test.gjs +++ b/tests/integration/components/class-field-description-test.gjs @@ -52,6 +52,98 @@ module('Integration | Component | class field description', function (hooks) { assert.dom('[data-test-module]').hasText('@ember/component'); }); + test('shows import example for static method on @-scoped package', async function (assert) { + const type = 'method'; + const field = { + name: 'hash', + static: 1, + itemtype: 'method', + class: '@ember/helper', + }; + + await render( + , + ); + + assert.dom('*').containsText("import { hash } from '@ember/helper';"); + }); + + test('shows import example for static method on rsvp', async function (assert) { + const type = 'method'; + const field = { + name: 'all', + static: 1, + itemtype: 'method', + class: 'rsvp', + }; + + await render( + , + ); + + assert.dom('*').containsText("import { all } from 'rsvp';"); + }); + + test('does not show import example when noimport flag exists', async function (assert) { + const type = 'method'; + const field = { + name: 'hash', + static: 1, + itemtype: 'method', + class: '@ember/helper', + noimport: '', + }; + + await render( + , + ); + + assert.dom('pre code').doesNotExist(); + }); + + test('does not show import example for non-importable package', async function (assert) { + const type = 'method'; + const field = { + name: 'someMethod', + static: 1, + itemtype: 'method', + class: 'SomeInternalClass', + }; + + await render( + , + ); + + assert.dom('pre code').doesNotExist(); + }); + + test('shows import example when field has exampleimport override', async function (assert) { + const type = 'method'; + const field = { + name: 'myMethod', + exampleimport: "import { myMethod } from 'some-package/internal';", + class: 'SomeInternalClass', + }; + + await render( + , + ); + + assert + .dom('*') + .containsText("import { myMethod } from 'some-package/internal';"); + }); + test('parameter props are displayed', async function (assert) { const type = 'method'; const field = { diff --git a/tests/integration/components/import-example-test.gjs b/tests/integration/components/import-example-test.gjs index 1bdb73c2..2402c23c 100644 --- a/tests/integration/components/import-example-test.gjs +++ b/tests/integration/components/import-example-test.gjs @@ -25,4 +25,20 @@ module('Integration | Component | import example', function (hooks) { ); assert.dom('*').hasText("import { uniqBy } from '@ember/object/computed';"); }); + + test('it renders a custom exampleimport override', async function (assert) { + await render( + , + ); + assert + .dom('*') + .hasText("import { someMethod } from '@ember/utils/legacy';"); + }); });