Skip to content

Commit 2b4f553

Browse files
committed
[Stimulus] Detect the lazy marker inside preserved /*! ... */ comments
Port symfony/ux#3702: recognise `stimulusFetch: 'lazy'` when it sits inside a preserved `/*! ... */` block comment, the form tsc/esbuild keep so the marker survives minification. Only the `/*` branch of the regex gains the optional `!`.
1 parent a9164b7 commit 2b4f553

4 files changed

Lines changed: 16 additions & 2 deletions

File tree

assets/src/core/stimulus.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@ interface ResolvedController {
5454
// which is why a stray `stimulusFetch: 'lazy'` sitting above the imports (or anywhere else) does
5555
// NOT flip the controller to lazy. It may be a block comment or a single-line one, single or
5656
// double quotes; a block comment may sit on the class's own line, a line comment must precede it.
57+
// A preserved block comment (`/*! ... */`, the form tsc/esbuild keep so the marker survives
58+
// minification) is recognised too.
5759
const LAZY_COMMENT_RE =
58-
/(?:\/\*\s*stimulusFetch:\s*['"]lazy['"]\s*\*\/|\/\/\s*stimulusFetch:\s*['"]lazy['"])\s*(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\b/i;
60+
/(?:\/\*!?\s*stimulusFetch:\s*['"]lazy['"]\s*\*\/|\/\/\s*stimulusFetch:\s*['"]lazy['"])\s*(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\b/i;
5961
const LOCAL_CONTROLLER_RE = /[-_]controller\.[jt]s$/;
6062

6163
export function generateControllersModule(opts: ResolvedStimulusOptions, root: string, isDev: boolean): string {

assets/test/core/stimulus.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ describe('generateControllersModule — local', () => {
5959
expect(src).toContain(posix(join(root, 'controllers/single_line_controller.js')));
6060
});
6161

62+
it('detects the lazy marker inside a preserved /*! ... */ comment', () => {
63+
const src = generateControllersModule(localOpts, root, false);
64+
// tsc/esbuild keep `/*! ... */` comments through minification, so the marker survives.
65+
expect(src).toContain(`"preserved-comment": () => import(`);
66+
expect(src).toContain(posix(join(root, 'controllers/preserved_comment_controller.js')));
67+
});
68+
6269
it('ignores a lazy marker that sits above the imports (it must be directly above the class)', () => {
6370
const src = generateControllersModule(localOpts, root, false);
6471
// The marker in `above_imports_controller.js` precedes the imports, not the class,
@@ -78,6 +85,7 @@ describe('generateControllersModule — local', () => {
7885
expect(src).toContain('export const eagerControllers = {}');
7986
expect(src).toContain('export const lazyControllers = {}');
8087
});
88+
8189
});
8290

8391
describe('generateControllersModule — identifier collision', () => {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/*! stimulusFetch: 'lazy' */
2+
export default class {}

doc/index.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ imports) — a block or a single-line comment both work:
119119
/* stimulusFetch: 'lazy' */
120120
export default class extends Controller {}
121121
122-
(``// stimulusFetch: 'lazy'`` on the line above the class works too.)
122+
(``// stimulusFetch: 'lazy'`` on the line above the class works too, as does a
123+
preserved ``/*! stimulusFetch: 'lazy' */`` comment — the form tsc and esbuild keep
124+
through minification.)
123125

124126
**Third-party UX packages.** Controllers declared in ``controllers.json`` are
125127
resolved from ``node_modules``, so install them with your package manager, the

0 commit comments

Comments
 (0)