@stencil/angular-output-target 1.1.1 → 1.2.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.
@@ -0,0 +1,8 @@
1
+ import type { CompilerCtx, ComponentCompilerMeta } from '@stencil/core/internal';
2
+ import type { OutputTargetAngular } from './types';
3
+ /**
4
+ * Generates the patch-transform-selectors.mjs script for Angular transformTag support.
5
+ * This script patches component selectors in the built Angular library to use the
6
+ * transformed tag names (e.g., 'my-component' -> 'v1-my-component').
7
+ */
8
+ export declare function generateTransformTagScript(compilerCtx: CompilerCtx, components: ComponentCompilerMeta[], outputTarget: OutputTargetAngular, packageName: string): Promise<void>;
@@ -0,0 +1,307 @@
1
+ import path from 'path';
2
+ import { dashToPascalCase } from './utils';
3
+ /**
4
+ * Generates the patch-transform-selectors.mjs script for Angular transformTag support.
5
+ * This script patches component selectors in the built Angular library to use the
6
+ * transformed tag names (e.g., 'my-component' -> 'v1-my-component').
7
+ */
8
+ export async function generateTransformTagScript(compilerCtx, components, outputTarget, packageName) {
9
+ const scriptsDirectory = path.join(path.dirname(outputTarget.directivesProxyFile), '../../scripts');
10
+ const customElementsDir = outputTarget.customElementsDir || 'dist/components';
11
+ const stencilImportPath = `${outputTarget.componentCorePackage}/${customElementsDir}/index.js`;
12
+ // Generate the mappings object
13
+ const mappings = components
14
+ .map((component) => {
15
+ const tagName = component.tagName;
16
+ const pascalName = dashToPascalCase(tagName);
17
+ return ` '${tagName}': '${pascalName}'`;
18
+ })
19
+ .join(',\n');
20
+ // Generate selector patcher script
21
+ const patchSelectorsContent = `#!/usr/bin/env node
22
+ /* eslint-disable */
23
+ /* tslint:disable */
24
+ /**
25
+ * Selector Patcher for transformTag support
26
+ *
27
+ * AUTO-GENERATED - DO NOT EDIT
28
+ *
29
+ * This script patches @Component selectors in the installed Angular component library
30
+ * to match your runtime tag transformer. Run this as a postinstall script in your app.
31
+ *
32
+ * Usage Option 1 - Config file (recommended for complex transformers):
33
+ * Create tag-transformer.config.mjs in your app root:
34
+ * export default (tag) => {
35
+ * if (tag.startsWith('my-transform-')) return \`v1-\${tag}\`;
36
+ * // ... complex logic
37
+ * return tag;
38
+ * };
39
+ *
40
+ * Then in package.json:
41
+ * "scripts": {
42
+ * "postinstall": "patch-transform-selectors"
43
+ * }
44
+ *
45
+ * Usage Option 2 - CLI argument (for simple transformers):
46
+ * "scripts": {
47
+ * "postinstall": "patch-transform-selectors \\"(tag) => tag.startsWith('my-transform-') ? \\\\\`v1-\\\${tag}\\\\\` : tag\\""
48
+ * }
49
+ */
50
+
51
+ import { readFileSync, writeFileSync, readdirSync, statSync, existsSync } from 'fs';
52
+ import { join, dirname } from 'path';
53
+ import { fileURLToPath, pathToFileURL } from 'url';
54
+
55
+ const __filename = fileURLToPath(import.meta.url);
56
+ const __dirname = dirname(__filename);
57
+
58
+ // Try to load transformer from config file or CLI argument
59
+ let TAG_TRANSFORMER;
60
+ let transformerArg;
61
+
62
+ // Option 1: Look for tag-transformer.config.mjs in the consuming app
63
+ const configPath = join(process.cwd(), 'tag-transformer.config.mjs');
64
+ if (existsSync(configPath)) {
65
+ console.log('[TransformTag] Loading transformer from tag-transformer.config.mjs');
66
+ try {
67
+ const configUrl = pathToFileURL(configPath).href;
68
+ const config = await import(configUrl);
69
+ TAG_TRANSFORMER = config.default;
70
+
71
+ if (typeof TAG_TRANSFORMER !== 'function') {
72
+ throw new Error('Config file must export a default function');
73
+ }
74
+
75
+ // Store as string for injection later
76
+ transformerArg = TAG_TRANSFORMER.toString();
77
+ console.log('[TransformTag] Loaded transformer from config file');
78
+ } catch (error) {
79
+ console.error('[TransformTag] Error loading tag-transformer.config.mjs:', error.message);
80
+ console.error('Make sure the file exports a default function.');
81
+ process.exit(1);
82
+ }
83
+ } else {
84
+ // Option 2: Fall back to CLI argument
85
+ transformerArg = process.argv[2];
86
+
87
+ if (!transformerArg) {
88
+ console.error('[TransformTag] Error: No transformer provided.');
89
+ console.error('');
90
+ console.error('Option 1 - Create tag-transformer.config.mjs in your app root:');
91
+ console.error(' export default (tag) => tag.startsWith(\\'my-\\') ? \`v1-\${tag}\` : tag;');
92
+ console.error('');
93
+ console.error('Option 2 - Pass transformer as CLI argument:');
94
+ console.error(' patch-transform-selectors "(tag) => tag.startsWith(\\'my-\\') ? \`v1-\${tag}\` : tag"');
95
+ process.exit(1);
96
+ }
97
+
98
+ // Evaluate the transformer string to get the function
99
+ try {
100
+ TAG_TRANSFORMER = eval(transformerArg);
101
+ if (typeof TAG_TRANSFORMER !== 'function') {
102
+ throw new Error('Transformer must be a function');
103
+ }
104
+ console.log('[TransformTag] Using transformer from CLI argument');
105
+ } catch (error) {
106
+ console.error('[TransformTag] Error: Invalid transformer function:', error.message);
107
+ console.error('The transformer must be a valid JavaScript function expression.');
108
+ console.error('Example: "(tag) => tag.startsWith(\\'my-\\') ? \`v1-\${tag}\` : tag"');
109
+ process.exit(1);
110
+ }
111
+ }
112
+
113
+ const TAG_MAPPINGS = {
114
+ ${mappings}
115
+ };
116
+
117
+ console.log('[TransformTag] Patching component selectors...');
118
+
119
+ try {
120
+ // Find the bundled JavaScript file (could be fesm2022, fesm2015, fesm5, etc.)
121
+ const parentDir = join(__dirname, '..');
122
+
123
+ // Find all .js/.mjs files in fesm* directories AND fesm*.js/mjs files at root
124
+ let bundlePaths = [];
125
+
126
+ try {
127
+ const entries = readdirSync(parentDir);
128
+ for (const entry of entries) {
129
+ const entryPath = join(parentDir, entry);
130
+ let stat;
131
+ try {
132
+ stat = statSync(entryPath);
133
+ } catch (e) {
134
+ continue;
135
+ }
136
+
137
+ // Check for fesm* directories
138
+ if (stat.isDirectory() && /^fesm/.test(entry)) {
139
+ try {
140
+ const fesmFiles = readdirSync(entryPath);
141
+ for (const file of fesmFiles) {
142
+ if (/\\.m?js$/.test(file)) {
143
+ bundlePaths.push(join(entryPath, file));
144
+ }
145
+ }
146
+ } catch (e) {
147
+ // Skip if can't read fesm directory
148
+ }
149
+ }
150
+ // Check for fesm*.js or fesm*.mjs files at root
151
+ else if (stat.isFile() && /^fesm.*\\.m?js$/.test(entry)) {
152
+ bundlePaths.push(entryPath);
153
+ }
154
+ }
155
+ } catch (e) {
156
+ console.error('[TransformTag] Could not read parent directory:', parentDir);
157
+ process.exit(1);
158
+ }
159
+
160
+ if (bundlePaths.length === 0) {
161
+ console.error('[TransformTag] Could not find any fesm* directories or files to patch.');
162
+ process.exit(1);
163
+ }
164
+
165
+ console.log('[TransformTag] Found bundles:', bundlePaths);
166
+
167
+ // Patch all bundled JavaScript files
168
+ let totalPatchedCount = 0;
169
+
170
+ for (const bundlePath of bundlePaths) {
171
+ let bundleContent;
172
+ try {
173
+ bundleContent = readFileSync(bundlePath, 'utf8');
174
+ } catch (e) {
175
+ console.error('[TransformTag] Could not read bundle:', bundlePath);
176
+ continue;
177
+ }
178
+
179
+ let patchedCount = 0;
180
+
181
+ for (const [originalTag, pascalName] of Object.entries(TAG_MAPPINGS)) {
182
+ const transformedTag = TAG_TRANSFORMER(originalTag);
183
+
184
+ // Only patch if the tag is actually transformed
185
+ if (transformedTag !== originalTag) {
186
+ // Update selector from original tag name to transformed tag name
187
+ // e.g., selector: 'my-transform-test' becomes selector: 'v1-my-transform-test'
188
+ const selectorRegex = new RegExp(
189
+ \`(selector:\\\\s*)(['"\\\`])\${originalTag}\\\\2\`,
190
+ 'g'
191
+ );
192
+
193
+ const newContent = bundleContent.replace(
194
+ selectorRegex,
195
+ \`$1'\${transformedTag}'\`
196
+ );
197
+
198
+ if (newContent !== bundleContent) {
199
+ bundleContent = newContent;
200
+ patchedCount++;
201
+ console.log(\`[TransformTag] Patched selector for \${originalTag} -> \${transformedTag}\`);
202
+ }
203
+ }
204
+ }
205
+
206
+ // Inject setTagTransformer call with the user's transformer
207
+ // Find the export statement and add the call before it
208
+ const exportMatch = bundleContent.match(/export \\{ setTagTransformer/);
209
+ if (exportMatch && patchedCount > 0) {
210
+ const transformerCode = \`
211
+ // Auto-injected by patch-transform-selectors
212
+ // Call setTagTransformer with the user-provided transformer
213
+ import { setTagTransformer as stencilSetTagTransformer } from '${stencilImportPath}';
214
+ stencilSetTagTransformer(\${transformerArg});
215
+ \`;
216
+ bundleContent = transformerCode + bundleContent;
217
+ console.log('[TransformTag] Injected setTagTransformer call into bundle');
218
+ }
219
+
220
+ // Write the patched bundle
221
+ if (patchedCount > 0) {
222
+ writeFileSync(bundlePath, bundleContent);
223
+ totalPatchedCount += patchedCount;
224
+ console.log(\`[TransformTag] Successfully patched \${patchedCount} component selectors in \${bundlePath}\`);
225
+ }
226
+ }
227
+
228
+ // Find and patch all .d.ts files
229
+ let totalTypePatchedCount = 0;
230
+
231
+ function patchTypeDefsInDir(dir) {
232
+ let files;
233
+ try {
234
+ files = readdirSync(dir);
235
+ } catch (e) {
236
+ return;
237
+ }
238
+
239
+ for (const file of files) {
240
+ const filePath = join(dir, file);
241
+ let stat;
242
+ try {
243
+ stat = statSync(filePath);
244
+ } catch (e) {
245
+ continue;
246
+ }
247
+
248
+ if (stat.isDirectory()) {
249
+ patchTypeDefsInDir(filePath);
250
+ } else if (file.endsWith('.d.ts')) {
251
+ let typeDefsContent;
252
+ try {
253
+ typeDefsContent = readFileSync(filePath, 'utf8');
254
+ } catch (e) {
255
+ continue;
256
+ }
257
+
258
+ let modified = false;
259
+
260
+ for (const [originalTag, pascalName] of Object.entries(TAG_MAPPINGS)) {
261
+ const transformedTag = TAG_TRANSFORMER(originalTag);
262
+
263
+ if (transformedTag !== originalTag) {
264
+ // Update selector in type definitions - format: ɵɵComponentDeclaration<ClassName, "tag-name", ...>
265
+ const typeDefRegex = new RegExp(
266
+ \`(ɵɵComponentDeclaration<\${pascalName},\\\\s*)"(\${originalTag})"\`,
267
+ 'g'
268
+ );
269
+
270
+ const newTypeContent = typeDefsContent.replace(
271
+ typeDefRegex,
272
+ \`$1"\${transformedTag}"\`
273
+ );
274
+
275
+ if (newTypeContent !== typeDefsContent) {
276
+ typeDefsContent = newTypeContent;
277
+ modified = true;
278
+ }
279
+ }
280
+ }
281
+
282
+ if (modified) {
283
+ writeFileSync(filePath, typeDefsContent);
284
+ totalTypePatchedCount++;
285
+ console.log(\`[TransformTag] Patched type definitions in: \${filePath}\`);
286
+ }
287
+ }
288
+ }
289
+ }
290
+
291
+ patchTypeDefsInDir(parentDir);
292
+
293
+ if (totalTypePatchedCount > 0) {
294
+ console.log(\`[TransformTag] Successfully patched selectors in \${totalTypePatchedCount} type definition files.\`);
295
+ }
296
+
297
+ if (totalPatchedCount === 0 && totalTypePatchedCount === 0) {
298
+ console.log('[TransformTag] No selectors needed patching.');
299
+ }
300
+ } catch (error) {
301
+ console.error('[TransformTag] Error patching selectors:', error.message);
302
+ console.error('Stack:', error.stack);
303
+ process.exit(1);
304
+ }
305
+ `;
306
+ await compilerCtx.fs.writeFile(path.join(scriptsDirectory, 'patch-transform-selectors.mjs'), patchSelectorsContent);
307
+ }
package/dist/index.cjs.js CHANGED
@@ -461,17 +461,348 @@ export class ${moduleClassName} { }`;
461
461
  return moduleDefinition;
462
462
  };
463
463
 
464
+ /**
465
+ * Generates the patch-transform-selectors.mjs script for Angular transformTag support.
466
+ * This script patches component selectors in the built Angular library to use the
467
+ * transformed tag names (e.g., 'my-component' -> 'v1-my-component').
468
+ */
469
+ async function generateTransformTagScript(compilerCtx, components, outputTarget, packageName) {
470
+ const scriptsDirectory = path__default["default"].join(path__default["default"].dirname(outputTarget.directivesProxyFile), '../../scripts');
471
+ const customElementsDir = outputTarget.customElementsDir || 'dist/components';
472
+ const stencilImportPath = `${outputTarget.componentCorePackage}/${customElementsDir}/index.js`;
473
+ // Generate the mappings object
474
+ const mappings = components
475
+ .map((component) => {
476
+ const tagName = component.tagName;
477
+ const pascalName = dashToPascalCase(tagName);
478
+ return ` '${tagName}': '${pascalName}'`;
479
+ })
480
+ .join(',\n');
481
+ // Generate selector patcher script
482
+ const patchSelectorsContent = `#!/usr/bin/env node
483
+ /* eslint-disable */
484
+ /* tslint:disable */
485
+ /**
486
+ * Selector Patcher for transformTag support
487
+ *
488
+ * AUTO-GENERATED - DO NOT EDIT
489
+ *
490
+ * This script patches @Component selectors in the installed Angular component library
491
+ * to match your runtime tag transformer. Run this as a postinstall script in your app.
492
+ *
493
+ * Usage Option 1 - Config file (recommended for complex transformers):
494
+ * Create tag-transformer.config.mjs in your app root:
495
+ * export default (tag) => {
496
+ * if (tag.startsWith('my-transform-')) return \`v1-\${tag}\`;
497
+ * // ... complex logic
498
+ * return tag;
499
+ * };
500
+ *
501
+ * Then in package.json:
502
+ * "scripts": {
503
+ * "postinstall": "patch-transform-selectors"
504
+ * }
505
+ *
506
+ * Usage Option 2 - CLI argument (for simple transformers):
507
+ * "scripts": {
508
+ * "postinstall": "patch-transform-selectors \\"(tag) => tag.startsWith('my-transform-') ? \\\\\`v1-\\\${tag}\\\\\` : tag\\""
509
+ * }
510
+ */
511
+
512
+ import { readFileSync, writeFileSync, readdirSync, statSync, existsSync } from 'fs';
513
+ import { join, dirname } from 'path';
514
+ import { fileURLToPath, pathToFileURL } from 'url';
515
+
516
+ const __filename = fileURLToPath(import.meta.url);
517
+ const __dirname = dirname(__filename);
518
+
519
+ // Try to load transformer from config file or CLI argument
520
+ let TAG_TRANSFORMER;
521
+ let transformerArg;
522
+
523
+ // Option 1: Look for tag-transformer.config.mjs in the consuming app
524
+ const configPath = join(process.cwd(), 'tag-transformer.config.mjs');
525
+ if (existsSync(configPath)) {
526
+ console.log('[TransformTag] Loading transformer from tag-transformer.config.mjs');
527
+ try {
528
+ const configUrl = pathToFileURL(configPath).href;
529
+ const config = await import(configUrl);
530
+ TAG_TRANSFORMER = config.default;
531
+
532
+ if (typeof TAG_TRANSFORMER !== 'function') {
533
+ throw new Error('Config file must export a default function');
534
+ }
535
+
536
+ // Store as string for injection later
537
+ transformerArg = TAG_TRANSFORMER.toString();
538
+ console.log('[TransformTag] Loaded transformer from config file');
539
+ } catch (error) {
540
+ console.error('[TransformTag] Error loading tag-transformer.config.mjs:', error.message);
541
+ console.error('Make sure the file exports a default function.');
542
+ process.exit(1);
543
+ }
544
+ } else {
545
+ // Option 2: Fall back to CLI argument
546
+ transformerArg = process.argv[2];
547
+
548
+ if (!transformerArg) {
549
+ console.error('[TransformTag] Error: No transformer provided.');
550
+ console.error('');
551
+ console.error('Option 1 - Create tag-transformer.config.mjs in your app root:');
552
+ console.error(' export default (tag) => tag.startsWith(\\'my-\\') ? \`v1-\${tag}\` : tag;');
553
+ console.error('');
554
+ console.error('Option 2 - Pass transformer as CLI argument:');
555
+ console.error(' patch-transform-selectors "(tag) => tag.startsWith(\\'my-\\') ? \`v1-\${tag}\` : tag"');
556
+ process.exit(1);
557
+ }
558
+
559
+ // Evaluate the transformer string to get the function
560
+ try {
561
+ TAG_TRANSFORMER = eval(transformerArg);
562
+ if (typeof TAG_TRANSFORMER !== 'function') {
563
+ throw new Error('Transformer must be a function');
564
+ }
565
+ console.log('[TransformTag] Using transformer from CLI argument');
566
+ } catch (error) {
567
+ console.error('[TransformTag] Error: Invalid transformer function:', error.message);
568
+ console.error('The transformer must be a valid JavaScript function expression.');
569
+ console.error('Example: "(tag) => tag.startsWith(\\'my-\\') ? \`v1-\${tag}\` : tag"');
570
+ process.exit(1);
571
+ }
572
+ }
573
+
574
+ const TAG_MAPPINGS = {
575
+ ${mappings}
576
+ };
577
+
578
+ console.log('[TransformTag] Patching component selectors...');
579
+
580
+ try {
581
+ // Find the bundled JavaScript file (could be fesm2022, fesm2015, fesm5, etc.)
582
+ const parentDir = join(__dirname, '..');
583
+
584
+ // Find all .js/.mjs files in fesm* directories AND fesm*.js/mjs files at root
585
+ let bundlePaths = [];
586
+
587
+ try {
588
+ const entries = readdirSync(parentDir);
589
+ for (const entry of entries) {
590
+ const entryPath = join(parentDir, entry);
591
+ let stat;
592
+ try {
593
+ stat = statSync(entryPath);
594
+ } catch (e) {
595
+ continue;
596
+ }
597
+
598
+ // Check for fesm* directories
599
+ if (stat.isDirectory() && /^fesm/.test(entry)) {
600
+ try {
601
+ const fesmFiles = readdirSync(entryPath);
602
+ for (const file of fesmFiles) {
603
+ if (/\\.m?js$/.test(file)) {
604
+ bundlePaths.push(join(entryPath, file));
605
+ }
606
+ }
607
+ } catch (e) {
608
+ // Skip if can't read fesm directory
609
+ }
610
+ }
611
+ // Check for fesm*.js or fesm*.mjs files at root
612
+ else if (stat.isFile() && /^fesm.*\\.m?js$/.test(entry)) {
613
+ bundlePaths.push(entryPath);
614
+ }
615
+ }
616
+ } catch (e) {
617
+ console.error('[TransformTag] Could not read parent directory:', parentDir);
618
+ process.exit(1);
619
+ }
620
+
621
+ if (bundlePaths.length === 0) {
622
+ console.error('[TransformTag] Could not find any fesm* directories or files to patch.');
623
+ process.exit(1);
624
+ }
625
+
626
+ console.log('[TransformTag] Found bundles:', bundlePaths);
627
+
628
+ // Patch all bundled JavaScript files
629
+ let totalPatchedCount = 0;
630
+
631
+ for (const bundlePath of bundlePaths) {
632
+ let bundleContent;
633
+ try {
634
+ bundleContent = readFileSync(bundlePath, 'utf8');
635
+ } catch (e) {
636
+ console.error('[TransformTag] Could not read bundle:', bundlePath);
637
+ continue;
638
+ }
639
+
640
+ let patchedCount = 0;
641
+
642
+ for (const [originalTag, pascalName] of Object.entries(TAG_MAPPINGS)) {
643
+ const transformedTag = TAG_TRANSFORMER(originalTag);
644
+
645
+ // Only patch if the tag is actually transformed
646
+ if (transformedTag !== originalTag) {
647
+ // Update selector from original tag name to transformed tag name
648
+ // e.g., selector: 'my-transform-test' becomes selector: 'v1-my-transform-test'
649
+ const selectorRegex = new RegExp(
650
+ \`(selector:\\\\s*)(['"\\\`])\${originalTag}\\\\2\`,
651
+ 'g'
652
+ );
653
+
654
+ const newContent = bundleContent.replace(
655
+ selectorRegex,
656
+ \`$1'\${transformedTag}'\`
657
+ );
658
+
659
+ if (newContent !== bundleContent) {
660
+ bundleContent = newContent;
661
+ patchedCount++;
662
+ console.log(\`[TransformTag] Patched selector for \${originalTag} -> \${transformedTag}\`);
663
+ }
664
+ }
665
+ }
666
+
667
+ // Inject setTagTransformer call with the user's transformer
668
+ // Find the export statement and add the call before it
669
+ const exportMatch = bundleContent.match(/export \\{ setTagTransformer/);
670
+ if (exportMatch && patchedCount > 0) {
671
+ const transformerCode = \`
672
+ // Auto-injected by patch-transform-selectors
673
+ // Call setTagTransformer with the user-provided transformer
674
+ import { setTagTransformer as stencilSetTagTransformer } from '${stencilImportPath}';
675
+ stencilSetTagTransformer(\${transformerArg});
676
+ \`;
677
+ bundleContent = transformerCode + bundleContent;
678
+ console.log('[TransformTag] Injected setTagTransformer call into bundle');
679
+ }
680
+
681
+ // Write the patched bundle
682
+ if (patchedCount > 0) {
683
+ writeFileSync(bundlePath, bundleContent);
684
+ totalPatchedCount += patchedCount;
685
+ console.log(\`[TransformTag] Successfully patched \${patchedCount} component selectors in \${bundlePath}\`);
686
+ }
687
+ }
688
+
689
+ // Find and patch all .d.ts files
690
+ let totalTypePatchedCount = 0;
691
+
692
+ function patchTypeDefsInDir(dir) {
693
+ let files;
694
+ try {
695
+ files = readdirSync(dir);
696
+ } catch (e) {
697
+ return;
698
+ }
699
+
700
+ for (const file of files) {
701
+ const filePath = join(dir, file);
702
+ let stat;
703
+ try {
704
+ stat = statSync(filePath);
705
+ } catch (e) {
706
+ continue;
707
+ }
708
+
709
+ if (stat.isDirectory()) {
710
+ patchTypeDefsInDir(filePath);
711
+ } else if (file.endsWith('.d.ts')) {
712
+ let typeDefsContent;
713
+ try {
714
+ typeDefsContent = readFileSync(filePath, 'utf8');
715
+ } catch (e) {
716
+ continue;
717
+ }
718
+
719
+ let modified = false;
720
+
721
+ for (const [originalTag, pascalName] of Object.entries(TAG_MAPPINGS)) {
722
+ const transformedTag = TAG_TRANSFORMER(originalTag);
723
+
724
+ if (transformedTag !== originalTag) {
725
+ // Update selector in type definitions - format: ɵɵComponentDeclaration<ClassName, "tag-name", ...>
726
+ const typeDefRegex = new RegExp(
727
+ \`(ɵɵComponentDeclaration<\${pascalName},\\\\s*)"(\${originalTag})"\`,
728
+ 'g'
729
+ );
730
+
731
+ const newTypeContent = typeDefsContent.replace(
732
+ typeDefRegex,
733
+ \`$1"\${transformedTag}"\`
734
+ );
735
+
736
+ if (newTypeContent !== typeDefsContent) {
737
+ typeDefsContent = newTypeContent;
738
+ modified = true;
739
+ }
740
+ }
741
+ }
742
+
743
+ if (modified) {
744
+ writeFileSync(filePath, typeDefsContent);
745
+ totalTypePatchedCount++;
746
+ console.log(\`[TransformTag] Patched type definitions in: \${filePath}\`);
747
+ }
748
+ }
749
+ }
750
+ }
751
+
752
+ patchTypeDefsInDir(parentDir);
753
+
754
+ if (totalTypePatchedCount > 0) {
755
+ console.log(\`[TransformTag] Successfully patched selectors in \${totalTypePatchedCount} type definition files.\`);
756
+ }
757
+
758
+ if (totalPatchedCount === 0 && totalTypePatchedCount === 0) {
759
+ console.log('[TransformTag] No selectors needed patching.');
760
+ }
761
+ } catch (error) {
762
+ console.error('[TransformTag] Error patching selectors:', error.message);
763
+ console.error('Stack:', error.stack);
764
+ process.exit(1);
765
+ }
766
+ `;
767
+ await compilerCtx.fs.writeFile(path__default["default"].join(scriptsDirectory, 'patch-transform-selectors.mjs'), patchSelectorsContent);
768
+ }
769
+
464
770
  async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
465
771
  const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
466
772
  const rootDir = config.rootDir;
467
773
  const pkgData = await readPackageJson(config, rootDir);
468
774
  const finalText = generateProxies(filteredComponents, pkgData, outputTarget, config.rootDir);
469
- await Promise.all([
775
+ const tasks = [
470
776
  compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText),
471
777
  copyResources(config, outputTarget),
472
778
  generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget),
473
779
  generateValueAccessors(compilerCtx, filteredComponents, outputTarget, config),
474
- ]);
780
+ ];
781
+ // Generate transformer script if transformTag is enabled
782
+ if (outputTarget.transformTag) {
783
+ // Read the Angular library's package.json to get its name
784
+ // directivesProxyFile is like: projects/library/src/directives/proxies.ts
785
+ // We need to go up to: projects/library/package.json
786
+ const angularLibraryDir = path__default["default"].dirname(path__default["default"].dirname(path__default["default"].dirname(outputTarget.directivesProxyFile)));
787
+ const angularPkgJsonPath = path__default["default"].join(angularLibraryDir, 'package.json');
788
+ let angularPackageName = '';
789
+ try {
790
+ const angularPkgJson = JSON.parse(await compilerCtx.fs.readFile(angularPkgJsonPath));
791
+ if (angularPkgJson.name) {
792
+ angularPackageName = angularPkgJson.name;
793
+ }
794
+ }
795
+ catch (e) {
796
+ throw new Error(`Could not read Angular library package.json at ${angularPkgJsonPath}. ` +
797
+ `The package name is required to generate the transformTag patch script.`);
798
+ }
799
+ if (!angularPackageName) {
800
+ throw new Error(`Angular library package.json at ${angularPkgJsonPath} does not have a "name" field. ` +
801
+ `The package name is required to generate the transformTag patch script.`);
802
+ }
803
+ tasks.push(generateTransformTagScript(compilerCtx, filteredComponents, outputTarget));
804
+ }
805
+ await Promise.all(tasks);
475
806
  }
476
807
  function getFilteredComponents(excludeComponents = [], cmps) {
477
808
  return sortBy(cmps, (cmp) => cmp.tagName).filter((c) => !excludeComponents.includes(c.tagName) && !c.internal);
package/dist/index.js CHANGED
@@ -453,17 +453,348 @@ export class ${moduleClassName} { }`;
453
453
  return moduleDefinition;
454
454
  };
455
455
 
456
+ /**
457
+ * Generates the patch-transform-selectors.mjs script for Angular transformTag support.
458
+ * This script patches component selectors in the built Angular library to use the
459
+ * transformed tag names (e.g., 'my-component' -> 'v1-my-component').
460
+ */
461
+ async function generateTransformTagScript(compilerCtx, components, outputTarget, packageName) {
462
+ const scriptsDirectory = path.join(path.dirname(outputTarget.directivesProxyFile), '../../scripts');
463
+ const customElementsDir = outputTarget.customElementsDir || 'dist/components';
464
+ const stencilImportPath = `${outputTarget.componentCorePackage}/${customElementsDir}/index.js`;
465
+ // Generate the mappings object
466
+ const mappings = components
467
+ .map((component) => {
468
+ const tagName = component.tagName;
469
+ const pascalName = dashToPascalCase(tagName);
470
+ return ` '${tagName}': '${pascalName}'`;
471
+ })
472
+ .join(',\n');
473
+ // Generate selector patcher script
474
+ const patchSelectorsContent = `#!/usr/bin/env node
475
+ /* eslint-disable */
476
+ /* tslint:disable */
477
+ /**
478
+ * Selector Patcher for transformTag support
479
+ *
480
+ * AUTO-GENERATED - DO NOT EDIT
481
+ *
482
+ * This script patches @Component selectors in the installed Angular component library
483
+ * to match your runtime tag transformer. Run this as a postinstall script in your app.
484
+ *
485
+ * Usage Option 1 - Config file (recommended for complex transformers):
486
+ * Create tag-transformer.config.mjs in your app root:
487
+ * export default (tag) => {
488
+ * if (tag.startsWith('my-transform-')) return \`v1-\${tag}\`;
489
+ * // ... complex logic
490
+ * return tag;
491
+ * };
492
+ *
493
+ * Then in package.json:
494
+ * "scripts": {
495
+ * "postinstall": "patch-transform-selectors"
496
+ * }
497
+ *
498
+ * Usage Option 2 - CLI argument (for simple transformers):
499
+ * "scripts": {
500
+ * "postinstall": "patch-transform-selectors \\"(tag) => tag.startsWith('my-transform-') ? \\\\\`v1-\\\${tag}\\\\\` : tag\\""
501
+ * }
502
+ */
503
+
504
+ import { readFileSync, writeFileSync, readdirSync, statSync, existsSync } from 'fs';
505
+ import { join, dirname } from 'path';
506
+ import { fileURLToPath, pathToFileURL } from 'url';
507
+
508
+ const __filename = fileURLToPath(import.meta.url);
509
+ const __dirname = dirname(__filename);
510
+
511
+ // Try to load transformer from config file or CLI argument
512
+ let TAG_TRANSFORMER;
513
+ let transformerArg;
514
+
515
+ // Option 1: Look for tag-transformer.config.mjs in the consuming app
516
+ const configPath = join(process.cwd(), 'tag-transformer.config.mjs');
517
+ if (existsSync(configPath)) {
518
+ console.log('[TransformTag] Loading transformer from tag-transformer.config.mjs');
519
+ try {
520
+ const configUrl = pathToFileURL(configPath).href;
521
+ const config = await import(configUrl);
522
+ TAG_TRANSFORMER = config.default;
523
+
524
+ if (typeof TAG_TRANSFORMER !== 'function') {
525
+ throw new Error('Config file must export a default function');
526
+ }
527
+
528
+ // Store as string for injection later
529
+ transformerArg = TAG_TRANSFORMER.toString();
530
+ console.log('[TransformTag] Loaded transformer from config file');
531
+ } catch (error) {
532
+ console.error('[TransformTag] Error loading tag-transformer.config.mjs:', error.message);
533
+ console.error('Make sure the file exports a default function.');
534
+ process.exit(1);
535
+ }
536
+ } else {
537
+ // Option 2: Fall back to CLI argument
538
+ transformerArg = process.argv[2];
539
+
540
+ if (!transformerArg) {
541
+ console.error('[TransformTag] Error: No transformer provided.');
542
+ console.error('');
543
+ console.error('Option 1 - Create tag-transformer.config.mjs in your app root:');
544
+ console.error(' export default (tag) => tag.startsWith(\\'my-\\') ? \`v1-\${tag}\` : tag;');
545
+ console.error('');
546
+ console.error('Option 2 - Pass transformer as CLI argument:');
547
+ console.error(' patch-transform-selectors "(tag) => tag.startsWith(\\'my-\\') ? \`v1-\${tag}\` : tag"');
548
+ process.exit(1);
549
+ }
550
+
551
+ // Evaluate the transformer string to get the function
552
+ try {
553
+ TAG_TRANSFORMER = eval(transformerArg);
554
+ if (typeof TAG_TRANSFORMER !== 'function') {
555
+ throw new Error('Transformer must be a function');
556
+ }
557
+ console.log('[TransformTag] Using transformer from CLI argument');
558
+ } catch (error) {
559
+ console.error('[TransformTag] Error: Invalid transformer function:', error.message);
560
+ console.error('The transformer must be a valid JavaScript function expression.');
561
+ console.error('Example: "(tag) => tag.startsWith(\\'my-\\') ? \`v1-\${tag}\` : tag"');
562
+ process.exit(1);
563
+ }
564
+ }
565
+
566
+ const TAG_MAPPINGS = {
567
+ ${mappings}
568
+ };
569
+
570
+ console.log('[TransformTag] Patching component selectors...');
571
+
572
+ try {
573
+ // Find the bundled JavaScript file (could be fesm2022, fesm2015, fesm5, etc.)
574
+ const parentDir = join(__dirname, '..');
575
+
576
+ // Find all .js/.mjs files in fesm* directories AND fesm*.js/mjs files at root
577
+ let bundlePaths = [];
578
+
579
+ try {
580
+ const entries = readdirSync(parentDir);
581
+ for (const entry of entries) {
582
+ const entryPath = join(parentDir, entry);
583
+ let stat;
584
+ try {
585
+ stat = statSync(entryPath);
586
+ } catch (e) {
587
+ continue;
588
+ }
589
+
590
+ // Check for fesm* directories
591
+ if (stat.isDirectory() && /^fesm/.test(entry)) {
592
+ try {
593
+ const fesmFiles = readdirSync(entryPath);
594
+ for (const file of fesmFiles) {
595
+ if (/\\.m?js$/.test(file)) {
596
+ bundlePaths.push(join(entryPath, file));
597
+ }
598
+ }
599
+ } catch (e) {
600
+ // Skip if can't read fesm directory
601
+ }
602
+ }
603
+ // Check for fesm*.js or fesm*.mjs files at root
604
+ else if (stat.isFile() && /^fesm.*\\.m?js$/.test(entry)) {
605
+ bundlePaths.push(entryPath);
606
+ }
607
+ }
608
+ } catch (e) {
609
+ console.error('[TransformTag] Could not read parent directory:', parentDir);
610
+ process.exit(1);
611
+ }
612
+
613
+ if (bundlePaths.length === 0) {
614
+ console.error('[TransformTag] Could not find any fesm* directories or files to patch.');
615
+ process.exit(1);
616
+ }
617
+
618
+ console.log('[TransformTag] Found bundles:', bundlePaths);
619
+
620
+ // Patch all bundled JavaScript files
621
+ let totalPatchedCount = 0;
622
+
623
+ for (const bundlePath of bundlePaths) {
624
+ let bundleContent;
625
+ try {
626
+ bundleContent = readFileSync(bundlePath, 'utf8');
627
+ } catch (e) {
628
+ console.error('[TransformTag] Could not read bundle:', bundlePath);
629
+ continue;
630
+ }
631
+
632
+ let patchedCount = 0;
633
+
634
+ for (const [originalTag, pascalName] of Object.entries(TAG_MAPPINGS)) {
635
+ const transformedTag = TAG_TRANSFORMER(originalTag);
636
+
637
+ // Only patch if the tag is actually transformed
638
+ if (transformedTag !== originalTag) {
639
+ // Update selector from original tag name to transformed tag name
640
+ // e.g., selector: 'my-transform-test' becomes selector: 'v1-my-transform-test'
641
+ const selectorRegex = new RegExp(
642
+ \`(selector:\\\\s*)(['"\\\`])\${originalTag}\\\\2\`,
643
+ 'g'
644
+ );
645
+
646
+ const newContent = bundleContent.replace(
647
+ selectorRegex,
648
+ \`$1'\${transformedTag}'\`
649
+ );
650
+
651
+ if (newContent !== bundleContent) {
652
+ bundleContent = newContent;
653
+ patchedCount++;
654
+ console.log(\`[TransformTag] Patched selector for \${originalTag} -> \${transformedTag}\`);
655
+ }
656
+ }
657
+ }
658
+
659
+ // Inject setTagTransformer call with the user's transformer
660
+ // Find the export statement and add the call before it
661
+ const exportMatch = bundleContent.match(/export \\{ setTagTransformer/);
662
+ if (exportMatch && patchedCount > 0) {
663
+ const transformerCode = \`
664
+ // Auto-injected by patch-transform-selectors
665
+ // Call setTagTransformer with the user-provided transformer
666
+ import { setTagTransformer as stencilSetTagTransformer } from '${stencilImportPath}';
667
+ stencilSetTagTransformer(\${transformerArg});
668
+ \`;
669
+ bundleContent = transformerCode + bundleContent;
670
+ console.log('[TransformTag] Injected setTagTransformer call into bundle');
671
+ }
672
+
673
+ // Write the patched bundle
674
+ if (patchedCount > 0) {
675
+ writeFileSync(bundlePath, bundleContent);
676
+ totalPatchedCount += patchedCount;
677
+ console.log(\`[TransformTag] Successfully patched \${patchedCount} component selectors in \${bundlePath}\`);
678
+ }
679
+ }
680
+
681
+ // Find and patch all .d.ts files
682
+ let totalTypePatchedCount = 0;
683
+
684
+ function patchTypeDefsInDir(dir) {
685
+ let files;
686
+ try {
687
+ files = readdirSync(dir);
688
+ } catch (e) {
689
+ return;
690
+ }
691
+
692
+ for (const file of files) {
693
+ const filePath = join(dir, file);
694
+ let stat;
695
+ try {
696
+ stat = statSync(filePath);
697
+ } catch (e) {
698
+ continue;
699
+ }
700
+
701
+ if (stat.isDirectory()) {
702
+ patchTypeDefsInDir(filePath);
703
+ } else if (file.endsWith('.d.ts')) {
704
+ let typeDefsContent;
705
+ try {
706
+ typeDefsContent = readFileSync(filePath, 'utf8');
707
+ } catch (e) {
708
+ continue;
709
+ }
710
+
711
+ let modified = false;
712
+
713
+ for (const [originalTag, pascalName] of Object.entries(TAG_MAPPINGS)) {
714
+ const transformedTag = TAG_TRANSFORMER(originalTag);
715
+
716
+ if (transformedTag !== originalTag) {
717
+ // Update selector in type definitions - format: ɵɵComponentDeclaration<ClassName, "tag-name", ...>
718
+ const typeDefRegex = new RegExp(
719
+ \`(ɵɵComponentDeclaration<\${pascalName},\\\\s*)"(\${originalTag})"\`,
720
+ 'g'
721
+ );
722
+
723
+ const newTypeContent = typeDefsContent.replace(
724
+ typeDefRegex,
725
+ \`$1"\${transformedTag}"\`
726
+ );
727
+
728
+ if (newTypeContent !== typeDefsContent) {
729
+ typeDefsContent = newTypeContent;
730
+ modified = true;
731
+ }
732
+ }
733
+ }
734
+
735
+ if (modified) {
736
+ writeFileSync(filePath, typeDefsContent);
737
+ totalTypePatchedCount++;
738
+ console.log(\`[TransformTag] Patched type definitions in: \${filePath}\`);
739
+ }
740
+ }
741
+ }
742
+ }
743
+
744
+ patchTypeDefsInDir(parentDir);
745
+
746
+ if (totalTypePatchedCount > 0) {
747
+ console.log(\`[TransformTag] Successfully patched selectors in \${totalTypePatchedCount} type definition files.\`);
748
+ }
749
+
750
+ if (totalPatchedCount === 0 && totalTypePatchedCount === 0) {
751
+ console.log('[TransformTag] No selectors needed patching.');
752
+ }
753
+ } catch (error) {
754
+ console.error('[TransformTag] Error patching selectors:', error.message);
755
+ console.error('Stack:', error.stack);
756
+ process.exit(1);
757
+ }
758
+ `;
759
+ await compilerCtx.fs.writeFile(path.join(scriptsDirectory, 'patch-transform-selectors.mjs'), patchSelectorsContent);
760
+ }
761
+
456
762
  async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
457
763
  const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
458
764
  const rootDir = config.rootDir;
459
765
  const pkgData = await readPackageJson(config, rootDir);
460
766
  const finalText = generateProxies(filteredComponents, pkgData, outputTarget, config.rootDir);
461
- await Promise.all([
767
+ const tasks = [
462
768
  compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText),
463
769
  copyResources(config, outputTarget),
464
770
  generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget),
465
771
  generateValueAccessors(compilerCtx, filteredComponents, outputTarget, config),
466
- ]);
772
+ ];
773
+ // Generate transformer script if transformTag is enabled
774
+ if (outputTarget.transformTag) {
775
+ // Read the Angular library's package.json to get its name
776
+ // directivesProxyFile is like: projects/library/src/directives/proxies.ts
777
+ // We need to go up to: projects/library/package.json
778
+ const angularLibraryDir = path.dirname(path.dirname(path.dirname(outputTarget.directivesProxyFile)));
779
+ const angularPkgJsonPath = path.join(angularLibraryDir, 'package.json');
780
+ let angularPackageName = '';
781
+ try {
782
+ const angularPkgJson = JSON.parse(await compilerCtx.fs.readFile(angularPkgJsonPath));
783
+ if (angularPkgJson.name) {
784
+ angularPackageName = angularPkgJson.name;
785
+ }
786
+ }
787
+ catch (e) {
788
+ throw new Error(`Could not read Angular library package.json at ${angularPkgJsonPath}. ` +
789
+ `The package name is required to generate the transformTag patch script.`);
790
+ }
791
+ if (!angularPackageName) {
792
+ throw new Error(`Angular library package.json at ${angularPkgJsonPath} does not have a "name" field. ` +
793
+ `The package name is required to generate the transformTag patch script.`);
794
+ }
795
+ tasks.push(generateTransformTagScript(compilerCtx, filteredComponents, outputTarget));
796
+ }
797
+ await Promise.all(tasks);
467
798
  }
468
799
  function getFilteredComponents(excludeComponents = [], cmps) {
469
800
  return sortBy(cmps, (cmp) => cmp.tagName).filter((c) => !excludeComponents.includes(c.tagName) && !c.internal);
@@ -4,17 +4,43 @@ import { createAngularComponentDefinition, createComponentTypeDefinition } from
4
4
  import { generateAngularDirectivesFile } from './generate-angular-directives-file';
5
5
  import generateValueAccessors from './generate-value-accessors';
6
6
  import { generateAngularModuleForComponent } from './generate-angular-modules';
7
+ import { generateTransformTagScript } from './generate-transformtag-script';
7
8
  export async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
8
9
  const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
9
10
  const rootDir = config.rootDir;
10
11
  const pkgData = await readPackageJson(config, rootDir);
11
12
  const finalText = generateProxies(filteredComponents, pkgData, outputTarget, config.rootDir);
12
- await Promise.all([
13
+ const tasks = [
13
14
  compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText),
14
15
  copyResources(config, outputTarget),
15
16
  generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget),
16
17
  generateValueAccessors(compilerCtx, filteredComponents, outputTarget, config),
17
- ]);
18
+ ];
19
+ // Generate transformer script if transformTag is enabled
20
+ if (outputTarget.transformTag) {
21
+ // Read the Angular library's package.json to get its name
22
+ // directivesProxyFile is like: projects/library/src/directives/proxies.ts
23
+ // We need to go up to: projects/library/package.json
24
+ const angularLibraryDir = path.dirname(path.dirname(path.dirname(outputTarget.directivesProxyFile)));
25
+ const angularPkgJsonPath = path.join(angularLibraryDir, 'package.json');
26
+ let angularPackageName = '';
27
+ try {
28
+ const angularPkgJson = JSON.parse(await compilerCtx.fs.readFile(angularPkgJsonPath));
29
+ if (angularPkgJson.name) {
30
+ angularPackageName = angularPkgJson.name;
31
+ }
32
+ }
33
+ catch (e) {
34
+ throw new Error(`Could not read Angular library package.json at ${angularPkgJsonPath}. ` +
35
+ `The package name is required to generate the transformTag patch script.`);
36
+ }
37
+ if (!angularPackageName) {
38
+ throw new Error(`Angular library package.json at ${angularPkgJsonPath} does not have a "name" field. ` +
39
+ `The package name is required to generate the transformTag patch script.`);
40
+ }
41
+ tasks.push(generateTransformTagScript(compilerCtx, filteredComponents, outputTarget, angularPackageName));
42
+ }
43
+ await Promise.all(tasks);
18
44
  }
19
45
  function getFilteredComponents(excludeComponents = [], cmps) {
20
46
  return sortBy(cmps, (cmp) => cmp.tagName).filter((c) => !excludeComponents.includes(c.tagName) && !c.internal);
package/dist/types.d.ts CHANGED
@@ -33,6 +33,42 @@ export interface OutputTargetAngular {
33
33
  * to type-check and show jsdocs when using the components in html-templates.
34
34
  */
35
35
  inlineProperties?: boolean;
36
+ /**
37
+ * Use `transformTag` to generate a build-time script.
38
+ * This script patches Angular `@Component` selectors in any installed Angular wrapper library
39
+ * enabling run-time tag transformation. Run the script as a postinstall script in your app.
40
+ *
41
+ * Setup:
42
+ * 1. Export `transformTag` and `setTagTransformer` from your component library:
43
+ * ```ts
44
+ * // src/index.ts
45
+ * export { transformTag, setTagTransformer } from '@stencil/core';
46
+ * ```
47
+ *
48
+ * 2. Add the patch script as a postinstall hook in your consuming app's package.json:
49
+ * ```json
50
+ * {
51
+ * "scripts": {
52
+ * "postinstall": "patch-transform-selectors \"(tag) => tag.startsWith('my-') ? `v1-${tag}` : tag\""
53
+ * }
54
+ * }
55
+ * ```
56
+ *
57
+ * 3. Configure the transformer at runtime in your Angular app with the SAME transformer:
58
+ * ```ts
59
+ * // main.ts
60
+ * import { setTagTransformer } from 'component-library';
61
+ * setTagTransformer((tag) => tag.startsWith('my-') ? `v1-${tag}` : tag);
62
+ * ```
63
+ *
64
+ * Angular relies on a static selector string so the patch script is used to
65
+ * modify the installed Angular component library for transformed tags.
66
+ * For example, if `my-button` transforms to `v1-my-button`, the selector
67
+ * will be patched from `selector: 'my-button'` to `selector: 'v1-my-button'`.
68
+ *
69
+ * @default false
70
+ */
71
+ transformTag?: boolean;
36
72
  }
37
73
  export type ValueAccessorTypes = 'text' | 'radio' | 'select' | 'number' | 'boolean';
38
74
  export interface ValueAccessorConfig {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stencil/angular-output-target",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Angular output target for @stencil/core components.",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.js",
@@ -50,7 +50,7 @@
50
50
  "devDependencies": {
51
51
  "@angular/core": "8.2.14",
52
52
  "@angular/forms": "8.2.14",
53
- "@stencil/core": "4.35.1",
53
+ "@stencil/core": "4.41.0",
54
54
  "@types/node": "^18.0.0",
55
55
  "npm-run-all2": "^6.2.4",
56
56
  "rimraf": "^5.0.0",
@@ -1,4 +1,4 @@
1
- import { Directive, ElementRef } from '@angular/core';
1
+ import { Directive, ElementRef, forwardRef } from '@angular/core';
2
2
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
3
3
 
4
4
  import { ValueAccessor } from './value-accessor';
@@ -12,7 +12,7 @@ import { ValueAccessor } from './value-accessor';
12
12
  providers: [
13
13
  {
14
14
  provide: NG_VALUE_ACCESSOR,
15
- useExisting: BooleanValueAccessor,
15
+ useExisting: forwardRef(() => BooleanValueAccessor),
16
16
  multi: true
17
17
  }
18
18
  ]<VALUE_ACCESSOR_STANDALONE>
@@ -1,4 +1,4 @@
1
- import { Directive, ElementRef } from '@angular/core';
1
+ import { Directive, ElementRef, forwardRef } from '@angular/core';
2
2
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
3
3
 
4
4
  import { ValueAccessor } from './value-accessor';
@@ -12,7 +12,7 @@ import { ValueAccessor } from './value-accessor';
12
12
  providers: [
13
13
  {
14
14
  provide: NG_VALUE_ACCESSOR,
15
- useExisting: NumericValueAccessor,
15
+ useExisting: forwardRef(() => NumericValueAccessor),
16
16
  multi: true
17
17
  }
18
18
  ]<VALUE_ACCESSOR_STANDALONE>
@@ -1,4 +1,4 @@
1
- import { Directive, ElementRef } from '@angular/core';
1
+ import { Directive, ElementRef, forwardRef } from '@angular/core';
2
2
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
3
3
 
4
4
  import { ValueAccessor } from './value-accessor';
@@ -12,7 +12,7 @@ import { ValueAccessor } from './value-accessor';
12
12
  providers: [
13
13
  {
14
14
  provide: NG_VALUE_ACCESSOR,
15
- useExisting: RadioValueAccessor,
15
+ useExisting: forwardRef(() => RadioValueAccessor),
16
16
  multi: true
17
17
  }
18
18
  ]<VALUE_ACCESSOR_STANDALONE>
@@ -1,4 +1,4 @@
1
- import { Directive, ElementRef } from '@angular/core';
1
+ import { Directive, ElementRef, forwardRef } from '@angular/core';
2
2
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
3
3
 
4
4
  import { ValueAccessor } from './value-accessor';
@@ -12,7 +12,7 @@ import { ValueAccessor } from './value-accessor';
12
12
  providers: [
13
13
  {
14
14
  provide: NG_VALUE_ACCESSOR,
15
- useExisting: SelectValueAccessor,
15
+ useExisting: forwardRef(() => SelectValueAccessor),
16
16
  multi: true
17
17
  }
18
18
  ]<VALUE_ACCESSOR_STANDALONE>
@@ -1,4 +1,4 @@
1
- import { Directive, ElementRef } from '@angular/core';
1
+ import { Directive, ElementRef, forwardRef } from '@angular/core';
2
2
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
3
3
 
4
4
  import { ValueAccessor } from './value-accessor';
@@ -12,7 +12,7 @@ import { ValueAccessor } from './value-accessor';
12
12
  providers: [
13
13
  {
14
14
  provide: NG_VALUE_ACCESSOR,
15
- useExisting: TextValueAccessor,
15
+ useExisting: forwardRef(() => TextValueAccessor),
16
16
  multi: true
17
17
  }
18
18
  ]<VALUE_ACCESSOR_STANDALONE>