@stencil/angular-output-target 1.3.1 → 1.4.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.
@@ -90,7 +90,7 @@ export const createAngularComponentDefinition = (tagName, inputs, methods, inclu
90
90
  .filter((event) => !event.internal)
91
91
  .map((event) => {
92
92
  const camelCaseOutput = event.name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
93
- const outputType = `EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`;
93
+ const outputType = `EventEmitter<${tagNameAsPascal}CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`;
94
94
  return `@Output() ${camelCaseOutput} = new ${outputType}();`;
95
95
  });
96
96
  const propertiesDeclarationText = [
@@ -164,8 +164,8 @@ const formatOutputType = (componentClassName, event) => {
164
164
  .replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (_, p1, p2) => {
165
165
  /**
166
166
  * Replaces a complex type reference within a generic type.
167
- * For example, remapping a type like `EventEmitter<CustomEvent<MyEvent<T>>>` to
168
- * `EventEmitter<CustomEvent<IMyComponentMyEvent<IMyComponentT>>>`.
167
+ * For example, remapping a type like `EventEmitter<MyComponentCustomEvent<MyEvent<T>>>` to
168
+ * `EventEmitter<MyComponentCustomEvent<IMyComponentMyEvent<IMyComponentT>>>`.
169
169
  */
170
170
  return [p1, prefixedTypeName, p2].join('');
171
171
  })
@@ -209,7 +209,7 @@ export const createComponentTypeDefinition = (outputType, tagNameAsPascal, event
209
209
  customElementsDir,
210
210
  outputType,
211
211
  });
212
- const eventTypes = publicEvents.map((event) => createPropertyDeclaration(event, `EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`));
212
+ const eventTypes = publicEvents.map((event) => createPropertyDeclaration(event, `EventEmitter<${tagNameAsPascal}CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`));
213
213
  const interfaceDeclaration = `export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {`;
214
214
  const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '') +
215
215
  `${interfaceDeclaration}${eventTypes.length === 0
@@ -1,5 +1,8 @@
1
1
  import { EOL } from 'os';
2
2
  import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ const __filename = fileURLToPath(import.meta.url);
5
+ const __dirname = path.dirname(__filename);
3
6
  import { OutputTypes } from './utils';
4
7
  export default async function generateValueAccessors(compilerCtx, components, outputTarget, config) {
5
8
  if (!Array.isArray(outputTarget.valueAccessorConfigs) || outputTarget.valueAccessorConfigs.length === 0) {
@@ -15,10 +18,13 @@ export default async function generateValueAccessors(compilerCtx, components, ou
15
18
  allElementSelectors = allAccessors[type].elementSelectors;
16
19
  allEventTargets = allAccessors[type].eventTargets;
17
20
  }
18
- return Object.assign(Object.assign({}, allAccessors), { [type]: {
21
+ return {
22
+ ...allAccessors,
23
+ [type]: {
19
24
  elementSelectors: allElementSelectors.concat(elementSelectors),
20
25
  eventTargets: allEventTargets.concat([[va.event, va.targetAttr]]),
21
- } });
26
+ },
27
+ };
22
28
  }, {});
23
29
  await Promise.all(Object.keys(normalizedValueAccessors).map(async (type) => {
24
30
  const valueAccessorType = type; // Object.keys converts to string
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var path = require('path');
6
+ var url = require('url');
6
7
  var os = require('os');
7
8
 
8
9
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
@@ -69,11 +70,10 @@ function relativeImport(pathFrom, pathTo, ext) {
69
70
  return normalizePath(`${relativePath}/${path__default["default"].basename(pathTo, ext)}`);
70
71
  }
71
72
  async function readPackageJson(config, rootDir) {
72
- var _a;
73
73
  const pkgJsonPath = path__default["default"].join(rootDir, 'package.json');
74
74
  let pkgJson;
75
75
  try {
76
- pkgJson = (await ((_a = config.sys) === null || _a === void 0 ? void 0 : _a.readFile(pkgJsonPath, 'utf8')));
76
+ pkgJson = (await config.sys?.readFile(pkgJsonPath, 'utf8'));
77
77
  }
78
78
  catch (e) {
79
79
  throw new Error(`Missing "package.json" file for distribution: ${pkgJsonPath}`);
@@ -127,6 +127,14 @@ const createComponentEventTypeImports = (componentTagName, events, options) => {
127
127
  const namedImports = new Set();
128
128
  const isCustomElementsBuild = isOutputTypeCustomElementsBuild(options.outputType);
129
129
  const importPathName = normalizePath(componentCorePackage) + (isCustomElementsBuild ? `/${customElementsDir}` : '');
130
+ /**
131
+ * Each component's events are typed with the per-component CustomEvent type
132
+ * generated by Stencil (e.g. `MyComponentCustomEvent<Detail>`), which carries a
133
+ * narrowed `target`. Import it once per component that emits public events.
134
+ */
135
+ if (events.length > 0) {
136
+ imports.push(`import type { ${componentTagName}CustomEvent } from '${importPathName}';`);
137
+ }
130
138
  events.forEach((event) => {
131
139
  Object.entries(event.complexType.references).forEach(([typeName, refObject]) => {
132
140
  if (refObject.location === 'local' || refObject.location === 'import') {
@@ -236,7 +244,7 @@ const createAngularComponentDefinition = (tagName, inputs, methods, includeImpor
236
244
  .filter((event) => !event.internal)
237
245
  .map((event) => {
238
246
  const camelCaseOutput = event.name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
239
- const outputType = `EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`;
247
+ const outputType = `EventEmitter<${tagNameAsPascal}CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`;
240
248
  return `@Output() ${camelCaseOutput} = new ${outputType}();`;
241
249
  });
242
250
  const propertiesDeclarationText = [
@@ -310,8 +318,8 @@ const formatOutputType = (componentClassName, event) => {
310
318
  .replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (_, p1, p2) => {
311
319
  /**
312
320
  * Replaces a complex type reference within a generic type.
313
- * For example, remapping a type like `EventEmitter<CustomEvent<MyEvent<T>>>` to
314
- * `EventEmitter<CustomEvent<IMyComponentMyEvent<IMyComponentT>>>`.
321
+ * For example, remapping a type like `EventEmitter<MyComponentCustomEvent<MyEvent<T>>>` to
322
+ * `EventEmitter<MyComponentCustomEvent<IMyComponentMyEvent<IMyComponentT>>>`.
315
323
  */
316
324
  return [p1, prefixedTypeName, p2].join('');
317
325
  })
@@ -355,7 +363,7 @@ const createComponentTypeDefinition = (outputType, tagNameAsPascal, events, comp
355
363
  customElementsDir,
356
364
  outputType,
357
365
  });
358
- const eventTypes = publicEvents.map((event) => createPropertyDeclaration(event, `EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`));
366
+ const eventTypes = publicEvents.map((event) => createPropertyDeclaration(event, `EventEmitter<${tagNameAsPascal}CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`));
359
367
  const interfaceDeclaration = `export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {`;
360
368
  const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '') +
361
369
  `${interfaceDeclaration}${eventTypes.length === 0
@@ -386,6 +394,8 @@ export const DIRECTIVES = [
386
394
  return compilerCtx.fs.writeFile(outputTarget.directivesArrayFile, c);
387
395
  }
388
396
 
397
+ const __filename$2 = url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || new URL('index.cjs', document.baseURI).href)));
398
+ const __dirname$2 = path__default["default"].dirname(__filename$2);
389
399
  async function generateValueAccessors(compilerCtx, components, outputTarget, config) {
390
400
  if (!Array.isArray(outputTarget.valueAccessorConfigs) || outputTarget.valueAccessorConfigs.length === 0) {
391
401
  return;
@@ -400,16 +410,19 @@ async function generateValueAccessors(compilerCtx, components, outputTarget, con
400
410
  allElementSelectors = allAccessors[type].elementSelectors;
401
411
  allEventTargets = allAccessors[type].eventTargets;
402
412
  }
403
- return Object.assign(Object.assign({}, allAccessors), { [type]: {
413
+ return {
414
+ ...allAccessors,
415
+ [type]: {
404
416
  elementSelectors: allElementSelectors.concat(elementSelectors),
405
417
  eventTargets: allEventTargets.concat([[va.event, va.targetAttr]]),
406
- } });
418
+ },
419
+ };
407
420
  }, {});
408
421
  await Promise.all(Object.keys(normalizedValueAccessors).map(async (type) => {
409
422
  const valueAccessorType = type; // Object.keys converts to string
410
423
  const targetFileName = `${type}-value-accessor.ts`;
411
424
  const targetFilePath = path__default["default"].join(targetDir, targetFileName);
412
- const srcFilePath = path__default["default"].join(__dirname, '../resources/control-value-accessors/', targetFileName);
425
+ const srcFilePath = path__default["default"].join(__dirname$2, '../resources/control-value-accessors/', targetFileName);
413
426
  const srcFileContents = await compilerCtx.fs.readFile(srcFilePath);
414
427
  const finalText = createValueAccessor(srcFileContents, normalizedValueAccessors[valueAccessorType], outputTarget.outputType);
415
428
  await compilerCtx.fs.writeFile(targetFilePath, finalText);
@@ -429,7 +442,7 @@ function copyResources$1(config, resourcesFilesToCopy, directory) {
429
442
  }
430
443
  const copyTasks = resourcesFilesToCopy.map((rf) => {
431
444
  return {
432
- src: path__default["default"].join(__dirname, '../resources/control-value-accessors/', rf),
445
+ src: path__default["default"].join(__dirname$2, '../resources/control-value-accessors/', rf),
433
446
  dest: path__default["default"].join(directory, rf),
434
447
  keepDirStructure: false,
435
448
  warn: false,
@@ -767,6 +780,8 @@ stencilSetTagTransformer(\${transformerArg});
767
780
  await compilerCtx.fs.writeFile(path__default["default"].join(scriptsDirectory, 'patch-transform-selectors.mjs'), patchSelectorsContent);
768
781
  }
769
782
 
783
+ const __filename$1 = url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || new URL('index.cjs', document.baseURI).href)));
784
+ const __dirname$1 = path__default["default"].dirname(__filename$1);
770
785
  async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
771
786
  const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
772
787
  const rootDir = config.rootDir;
@@ -783,7 +798,7 @@ async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components
783
798
  const proxiesDir = path__default["default"].dirname(outputTarget.directivesProxyFile);
784
799
  for (const component of filteredComponents) {
785
800
  const componentFile = path__default["default"].join(proxiesDir, `${component.tagName}.ts`);
786
- const componentText = generateComponentProxy(component, pkgData, outputTarget, rootDir);
801
+ const componentText = generateComponentProxy(component, pkgData, outputTarget, rootDir, config);
787
802
  tasks.push(compilerCtx.fs.writeFile(componentFile, componentText));
788
803
  }
789
804
  // Generate barrel file that re-exports all components
@@ -794,7 +809,7 @@ async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components
794
809
  }
795
810
  else {
796
811
  // Generate single file with all components (original behavior)
797
- const finalText = generateProxies(filteredComponents, pkgData, outputTarget, rootDir);
812
+ const finalText = generateProxies(filteredComponents, pkgData, outputTarget, rootDir, config);
798
813
  tasks.push(compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText));
799
814
  tasks.push(generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget));
800
815
  }
@@ -831,7 +846,7 @@ async function copyResources(config, outputTarget) {
831
846
  if (!config.sys || !config.sys.copy || !config.sys.glob) {
832
847
  throw new Error('stencil is not properly initialized at this step. Notify the developer');
833
848
  }
834
- const srcDirectory = path__default["default"].join(__dirname, '..', 'angular-component-lib');
849
+ const srcDirectory = path__default["default"].join(__dirname$1, '..', 'angular-component-lib');
835
850
  const destDirectory = path__default["default"].join(path__default["default"].dirname(outputTarget.directivesProxyFile), 'angular-component-lib');
836
851
  return config.sys.copy([
837
852
  {
@@ -843,7 +858,7 @@ async function copyResources(config, outputTarget) {
843
858
  },
844
859
  ], srcDirectory);
845
860
  }
846
- function generateProxies(components, pkgData, outputTarget, rootDir) {
861
+ function generateProxies(components, pkgData, outputTarget, rootDir, config) {
847
862
  const distTypesDir = path__default["default"].dirname(pkgData.types);
848
863
  const dtsFilePath = path__default["default"].join(rootDir, distTypesDir, GENERATED_DTS);
849
864
  const { outputType } = outputTarget;
@@ -879,10 +894,9 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
879
894
  * otherwise we risk bundlers pulling in lazy loaded imports.
880
895
  */
881
896
  const generateTypeImports = () => {
882
- let importLocation = outputTarget.componentCorePackage
883
- ? normalizePath(outputTarget.componentCorePackage)
897
+ const importLocation = outputTarget.componentCorePackage
898
+ ? getPathToComponentTypes(config, outputTarget)
884
899
  : normalizePath(componentsTypeFile);
885
- importLocation += isCustomElementsBuild ? `/${outputTarget.customElementsDir}` : '';
886
900
  return `import ${isCustomElementsBuild ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
887
901
  };
888
902
  const typeImports = generateTypeImports();
@@ -903,13 +917,10 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
903
917
  const proxyFileOutput = [];
904
918
  const filterInternalProps = (prop) => !prop.internal;
905
919
  // Ensure that virtual properties has required as false.
906
- const mapInputProp = (prop) => {
907
- var _a;
908
- return ({
909
- name: prop.name,
910
- required: (_a = prop.required) !== null && _a !== void 0 ? _a : false,
911
- });
912
- };
920
+ const mapInputProp = (prop) => ({
921
+ name: prop.name,
922
+ required: prop.required ?? false,
923
+ });
913
924
  const { componentCorePackage, customElementsDir } = outputTarget;
914
925
  for (let cmpMeta of components) {
915
926
  const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
@@ -948,8 +959,7 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
948
959
  /**
949
960
  * Generate a single component proxy file for ES modules output
950
961
  */
951
- function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir) {
952
- var _a;
962
+ function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir, config) {
953
963
  const { outputType, componentCorePackage, customElementsDir } = outputTarget;
954
964
  const distTypesDir = path__default["default"].dirname(pkgData.types);
955
965
  const dtsFilePath = path__default["default"].join(rootDir, distTypesDir, GENERATED_DTS);
@@ -958,7 +968,7 @@ function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir) {
958
968
  const isCustomElementsBuild = isOutputTypeCustomElementsBuild(outputType);
959
969
  const isStandaloneBuild = outputType === OutputTypes.Standalone;
960
970
  const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
961
- const hasOutputs = (_a = cmpMeta.events) === null || _a === void 0 ? void 0 : _a.some((event) => !event.internal);
971
+ const hasOutputs = cmpMeta.events?.some((event) => !event.internal);
962
972
  // Angular core imports for this component
963
973
  const angularCoreImports = ['ChangeDetectionStrategy', 'ChangeDetectorRef', 'Component', 'ElementRef', 'NgZone'];
964
974
  if (hasOutputs) {
@@ -973,8 +983,9 @@ ${createImportStatement(angularCoreImports, '@angular/core')}
973
983
 
974
984
  ${createImportStatement(['ProxyCmp'], './angular-component-lib/utils')}\n`;
975
985
  // Type imports
976
- let importLocation = componentCorePackage ? normalizePath(componentCorePackage) : normalizePath(componentsTypeFile);
977
- importLocation += isCustomElementsBuild ? `/${customElementsDir}` : '';
986
+ const importLocation = componentCorePackage
987
+ ? getPathToComponentTypes(config, outputTarget)
988
+ : normalizePath(componentsTypeFile);
978
989
  const typeImports = `import ${isCustomElementsBuild ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
979
990
  // defineCustomElement import
980
991
  let sourceImport = '';
@@ -983,13 +994,10 @@ ${createImportStatement(['ProxyCmp'], './angular-component-lib/utils')}\n`;
983
994
  }
984
995
  // Generate component definition
985
996
  const filterInternalProps = (prop) => !prop.internal;
986
- const mapInputProp = (prop) => {
987
- var _a;
988
- return ({
989
- name: prop.name,
990
- required: (_a = prop.required) !== null && _a !== void 0 ? _a : false,
991
- });
992
- };
997
+ const mapInputProp = (prop) => ({
998
+ name: prop.name,
999
+ required: prop.required ?? false,
1000
+ });
993
1001
  const internalProps = [];
994
1002
  if (cmpMeta.properties) {
995
1003
  internalProps.push(...cmpMeta.properties.filter(filterInternalProps));
@@ -1035,6 +1043,22 @@ function generateBarrelFile(components, outputTarget) {
1035
1043
  .join('\n');
1036
1044
  return header + exports + '\n';
1037
1045
  }
1046
+ function getPathToComponentTypes(config, outputTarget) {
1047
+ const basePkg = outputTarget.componentCorePackage || '';
1048
+ // in v5, all types (including components.d.ts) are generated in the dist/types directory
1049
+ const typesTarget = config.outputTargets?.find((o) => o.type === 'types');
1050
+ if (typesTarget) {
1051
+ const rawDir = typesTarget.dir || 'dist/types';
1052
+ const relDir = config.rootDir && path__default["default"].isAbsolute(rawDir) ? path__default["default"].relative(config.rootDir, rawDir) : rawDir;
1053
+ return normalizePath(path__default["default"].join(basePkg, relDir, 'components'));
1054
+ }
1055
+ // v4: append customElementsDir for custom elements build, otherwise package root
1056
+ const isCustomElementsBuild = isOutputTypeCustomElementsBuild(outputTarget.outputType);
1057
+ if (isCustomElementsBuild && outputTarget.customElementsDir) {
1058
+ return normalizePath(path__default["default"].join(basePkg, outputTarget.customElementsDir));
1059
+ }
1060
+ return normalizePath(basePkg);
1061
+ }
1038
1062
  const GENERATED_DTS = 'components.d.ts';
1039
1063
  const IMPORT_TYPES = 'Components';
1040
1064
 
@@ -1054,8 +1078,13 @@ const angularOutputTarget = (outputTarget) => {
1054
1078
  };
1055
1079
  };
1056
1080
  function normalizeOutputTarget(config, outputTarget) {
1057
- var _a, _b;
1058
- const results = Object.assign(Object.assign({}, outputTarget), { excludeComponents: outputTarget.excludeComponents || [], valueAccessorConfigs: outputTarget.valueAccessorConfigs || [], customElementsDir: (_a = outputTarget.customElementsDir) !== null && _a !== void 0 ? _a : 'components', outputType: (_b = outputTarget.outputType) !== null && _b !== void 0 ? _b : OutputTypes.Standalone });
1081
+ const results = {
1082
+ ...outputTarget,
1083
+ excludeComponents: outputTarget.excludeComponents || [],
1084
+ valueAccessorConfigs: outputTarget.valueAccessorConfigs || [],
1085
+ customElementsDir: outputTarget.customElementsDir ?? 'components',
1086
+ outputType: outputTarget.outputType ?? OutputTypes.Standalone,
1087
+ };
1059
1088
  if (config.rootDir == null) {
1060
1089
  throw new Error('rootDir is not set and it should be set by stencil itself');
1061
1090
  }
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import path from 'path';
2
+ import { fileURLToPath } from 'url';
2
3
  import { EOL } from 'os';
3
4
 
4
5
  const OutputTypes = {
@@ -61,11 +62,10 @@ function relativeImport(pathFrom, pathTo, ext) {
61
62
  return normalizePath(`${relativePath}/${path.basename(pathTo, ext)}`);
62
63
  }
63
64
  async function readPackageJson(config, rootDir) {
64
- var _a;
65
65
  const pkgJsonPath = path.join(rootDir, 'package.json');
66
66
  let pkgJson;
67
67
  try {
68
- pkgJson = (await ((_a = config.sys) === null || _a === void 0 ? void 0 : _a.readFile(pkgJsonPath, 'utf8')));
68
+ pkgJson = (await config.sys?.readFile(pkgJsonPath, 'utf8'));
69
69
  }
70
70
  catch (e) {
71
71
  throw new Error(`Missing "package.json" file for distribution: ${pkgJsonPath}`);
@@ -119,6 +119,14 @@ const createComponentEventTypeImports = (componentTagName, events, options) => {
119
119
  const namedImports = new Set();
120
120
  const isCustomElementsBuild = isOutputTypeCustomElementsBuild(options.outputType);
121
121
  const importPathName = normalizePath(componentCorePackage) + (isCustomElementsBuild ? `/${customElementsDir}` : '');
122
+ /**
123
+ * Each component's events are typed with the per-component CustomEvent type
124
+ * generated by Stencil (e.g. `MyComponentCustomEvent<Detail>`), which carries a
125
+ * narrowed `target`. Import it once per component that emits public events.
126
+ */
127
+ if (events.length > 0) {
128
+ imports.push(`import type { ${componentTagName}CustomEvent } from '${importPathName}';`);
129
+ }
122
130
  events.forEach((event) => {
123
131
  Object.entries(event.complexType.references).forEach(([typeName, refObject]) => {
124
132
  if (refObject.location === 'local' || refObject.location === 'import') {
@@ -228,7 +236,7 @@ const createAngularComponentDefinition = (tagName, inputs, methods, includeImpor
228
236
  .filter((event) => !event.internal)
229
237
  .map((event) => {
230
238
  const camelCaseOutput = event.name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
231
- const outputType = `EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`;
239
+ const outputType = `EventEmitter<${tagNameAsPascal}CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`;
232
240
  return `@Output() ${camelCaseOutput} = new ${outputType}();`;
233
241
  });
234
242
  const propertiesDeclarationText = [
@@ -302,8 +310,8 @@ const formatOutputType = (componentClassName, event) => {
302
310
  .replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (_, p1, p2) => {
303
311
  /**
304
312
  * Replaces a complex type reference within a generic type.
305
- * For example, remapping a type like `EventEmitter<CustomEvent<MyEvent<T>>>` to
306
- * `EventEmitter<CustomEvent<IMyComponentMyEvent<IMyComponentT>>>`.
313
+ * For example, remapping a type like `EventEmitter<MyComponentCustomEvent<MyEvent<T>>>` to
314
+ * `EventEmitter<MyComponentCustomEvent<IMyComponentMyEvent<IMyComponentT>>>`.
307
315
  */
308
316
  return [p1, prefixedTypeName, p2].join('');
309
317
  })
@@ -347,7 +355,7 @@ const createComponentTypeDefinition = (outputType, tagNameAsPascal, events, comp
347
355
  customElementsDir,
348
356
  outputType,
349
357
  });
350
- const eventTypes = publicEvents.map((event) => createPropertyDeclaration(event, `EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`));
358
+ const eventTypes = publicEvents.map((event) => createPropertyDeclaration(event, `EventEmitter<${tagNameAsPascal}CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>`));
351
359
  const interfaceDeclaration = `export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {`;
352
360
  const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '') +
353
361
  `${interfaceDeclaration}${eventTypes.length === 0
@@ -378,6 +386,8 @@ export const DIRECTIVES = [
378
386
  return compilerCtx.fs.writeFile(outputTarget.directivesArrayFile, c);
379
387
  }
380
388
 
389
+ const __filename$1 = fileURLToPath(import.meta.url);
390
+ const __dirname$1 = path.dirname(__filename$1);
381
391
  async function generateValueAccessors(compilerCtx, components, outputTarget, config) {
382
392
  if (!Array.isArray(outputTarget.valueAccessorConfigs) || outputTarget.valueAccessorConfigs.length === 0) {
383
393
  return;
@@ -392,16 +402,19 @@ async function generateValueAccessors(compilerCtx, components, outputTarget, con
392
402
  allElementSelectors = allAccessors[type].elementSelectors;
393
403
  allEventTargets = allAccessors[type].eventTargets;
394
404
  }
395
- return Object.assign(Object.assign({}, allAccessors), { [type]: {
405
+ return {
406
+ ...allAccessors,
407
+ [type]: {
396
408
  elementSelectors: allElementSelectors.concat(elementSelectors),
397
409
  eventTargets: allEventTargets.concat([[va.event, va.targetAttr]]),
398
- } });
410
+ },
411
+ };
399
412
  }, {});
400
413
  await Promise.all(Object.keys(normalizedValueAccessors).map(async (type) => {
401
414
  const valueAccessorType = type; // Object.keys converts to string
402
415
  const targetFileName = `${type}-value-accessor.ts`;
403
416
  const targetFilePath = path.join(targetDir, targetFileName);
404
- const srcFilePath = path.join(__dirname, '../resources/control-value-accessors/', targetFileName);
417
+ const srcFilePath = path.join(__dirname$1, '../resources/control-value-accessors/', targetFileName);
405
418
  const srcFileContents = await compilerCtx.fs.readFile(srcFilePath);
406
419
  const finalText = createValueAccessor(srcFileContents, normalizedValueAccessors[valueAccessorType], outputTarget.outputType);
407
420
  await compilerCtx.fs.writeFile(targetFilePath, finalText);
@@ -421,7 +434,7 @@ function copyResources$1(config, resourcesFilesToCopy, directory) {
421
434
  }
422
435
  const copyTasks = resourcesFilesToCopy.map((rf) => {
423
436
  return {
424
- src: path.join(__dirname, '../resources/control-value-accessors/', rf),
437
+ src: path.join(__dirname$1, '../resources/control-value-accessors/', rf),
425
438
  dest: path.join(directory, rf),
426
439
  keepDirStructure: false,
427
440
  warn: false,
@@ -759,6 +772,8 @@ stencilSetTagTransformer(\${transformerArg});
759
772
  await compilerCtx.fs.writeFile(path.join(scriptsDirectory, 'patch-transform-selectors.mjs'), patchSelectorsContent);
760
773
  }
761
774
 
775
+ const __filename = fileURLToPath(import.meta.url);
776
+ const __dirname = path.dirname(__filename);
762
777
  async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
763
778
  const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
764
779
  const rootDir = config.rootDir;
@@ -775,7 +790,7 @@ async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components
775
790
  const proxiesDir = path.dirname(outputTarget.directivesProxyFile);
776
791
  for (const component of filteredComponents) {
777
792
  const componentFile = path.join(proxiesDir, `${component.tagName}.ts`);
778
- const componentText = generateComponentProxy(component, pkgData, outputTarget, rootDir);
793
+ const componentText = generateComponentProxy(component, pkgData, outputTarget, rootDir, config);
779
794
  tasks.push(compilerCtx.fs.writeFile(componentFile, componentText));
780
795
  }
781
796
  // Generate barrel file that re-exports all components
@@ -786,7 +801,7 @@ async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components
786
801
  }
787
802
  else {
788
803
  // Generate single file with all components (original behavior)
789
- const finalText = generateProxies(filteredComponents, pkgData, outputTarget, rootDir);
804
+ const finalText = generateProxies(filteredComponents, pkgData, outputTarget, rootDir, config);
790
805
  tasks.push(compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText));
791
806
  tasks.push(generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget));
792
807
  }
@@ -835,7 +850,7 @@ async function copyResources(config, outputTarget) {
835
850
  },
836
851
  ], srcDirectory);
837
852
  }
838
- function generateProxies(components, pkgData, outputTarget, rootDir) {
853
+ function generateProxies(components, pkgData, outputTarget, rootDir, config) {
839
854
  const distTypesDir = path.dirname(pkgData.types);
840
855
  const dtsFilePath = path.join(rootDir, distTypesDir, GENERATED_DTS);
841
856
  const { outputType } = outputTarget;
@@ -871,10 +886,9 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
871
886
  * otherwise we risk bundlers pulling in lazy loaded imports.
872
887
  */
873
888
  const generateTypeImports = () => {
874
- let importLocation = outputTarget.componentCorePackage
875
- ? normalizePath(outputTarget.componentCorePackage)
889
+ const importLocation = outputTarget.componentCorePackage
890
+ ? getPathToComponentTypes(config, outputTarget)
876
891
  : normalizePath(componentsTypeFile);
877
- importLocation += isCustomElementsBuild ? `/${outputTarget.customElementsDir}` : '';
878
892
  return `import ${isCustomElementsBuild ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
879
893
  };
880
894
  const typeImports = generateTypeImports();
@@ -895,13 +909,10 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
895
909
  const proxyFileOutput = [];
896
910
  const filterInternalProps = (prop) => !prop.internal;
897
911
  // Ensure that virtual properties has required as false.
898
- const mapInputProp = (prop) => {
899
- var _a;
900
- return ({
901
- name: prop.name,
902
- required: (_a = prop.required) !== null && _a !== void 0 ? _a : false,
903
- });
904
- };
912
+ const mapInputProp = (prop) => ({
913
+ name: prop.name,
914
+ required: prop.required ?? false,
915
+ });
905
916
  const { componentCorePackage, customElementsDir } = outputTarget;
906
917
  for (let cmpMeta of components) {
907
918
  const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
@@ -940,8 +951,7 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
940
951
  /**
941
952
  * Generate a single component proxy file for ES modules output
942
953
  */
943
- function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir) {
944
- var _a;
954
+ function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir, config) {
945
955
  const { outputType, componentCorePackage, customElementsDir } = outputTarget;
946
956
  const distTypesDir = path.dirname(pkgData.types);
947
957
  const dtsFilePath = path.join(rootDir, distTypesDir, GENERATED_DTS);
@@ -950,7 +960,7 @@ function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir) {
950
960
  const isCustomElementsBuild = isOutputTypeCustomElementsBuild(outputType);
951
961
  const isStandaloneBuild = outputType === OutputTypes.Standalone;
952
962
  const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
953
- const hasOutputs = (_a = cmpMeta.events) === null || _a === void 0 ? void 0 : _a.some((event) => !event.internal);
963
+ const hasOutputs = cmpMeta.events?.some((event) => !event.internal);
954
964
  // Angular core imports for this component
955
965
  const angularCoreImports = ['ChangeDetectionStrategy', 'ChangeDetectorRef', 'Component', 'ElementRef', 'NgZone'];
956
966
  if (hasOutputs) {
@@ -965,8 +975,9 @@ ${createImportStatement(angularCoreImports, '@angular/core')}
965
975
 
966
976
  ${createImportStatement(['ProxyCmp'], './angular-component-lib/utils')}\n`;
967
977
  // Type imports
968
- let importLocation = componentCorePackage ? normalizePath(componentCorePackage) : normalizePath(componentsTypeFile);
969
- importLocation += isCustomElementsBuild ? `/${customElementsDir}` : '';
978
+ const importLocation = componentCorePackage
979
+ ? getPathToComponentTypes(config, outputTarget)
980
+ : normalizePath(componentsTypeFile);
970
981
  const typeImports = `import ${isCustomElementsBuild ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
971
982
  // defineCustomElement import
972
983
  let sourceImport = '';
@@ -975,13 +986,10 @@ ${createImportStatement(['ProxyCmp'], './angular-component-lib/utils')}\n`;
975
986
  }
976
987
  // Generate component definition
977
988
  const filterInternalProps = (prop) => !prop.internal;
978
- const mapInputProp = (prop) => {
979
- var _a;
980
- return ({
981
- name: prop.name,
982
- required: (_a = prop.required) !== null && _a !== void 0 ? _a : false,
983
- });
984
- };
989
+ const mapInputProp = (prop) => ({
990
+ name: prop.name,
991
+ required: prop.required ?? false,
992
+ });
985
993
  const internalProps = [];
986
994
  if (cmpMeta.properties) {
987
995
  internalProps.push(...cmpMeta.properties.filter(filterInternalProps));
@@ -1027,6 +1035,22 @@ function generateBarrelFile(components, outputTarget) {
1027
1035
  .join('\n');
1028
1036
  return header + exports + '\n';
1029
1037
  }
1038
+ function getPathToComponentTypes(config, outputTarget) {
1039
+ const basePkg = outputTarget.componentCorePackage || '';
1040
+ // in v5, all types (including components.d.ts) are generated in the dist/types directory
1041
+ const typesTarget = config.outputTargets?.find((o) => o.type === 'types');
1042
+ if (typesTarget) {
1043
+ const rawDir = typesTarget.dir || 'dist/types';
1044
+ const relDir = config.rootDir && path.isAbsolute(rawDir) ? path.relative(config.rootDir, rawDir) : rawDir;
1045
+ return normalizePath(path.join(basePkg, relDir, 'components'));
1046
+ }
1047
+ // v4: append customElementsDir for custom elements build, otherwise package root
1048
+ const isCustomElementsBuild = isOutputTypeCustomElementsBuild(outputTarget.outputType);
1049
+ if (isCustomElementsBuild && outputTarget.customElementsDir) {
1050
+ return normalizePath(path.join(basePkg, outputTarget.customElementsDir));
1051
+ }
1052
+ return normalizePath(basePkg);
1053
+ }
1030
1054
  const GENERATED_DTS = 'components.d.ts';
1031
1055
  const IMPORT_TYPES = 'Components';
1032
1056
 
@@ -1046,8 +1070,13 @@ const angularOutputTarget = (outputTarget) => {
1046
1070
  };
1047
1071
  };
1048
1072
  function normalizeOutputTarget(config, outputTarget) {
1049
- var _a, _b;
1050
- const results = Object.assign(Object.assign({}, outputTarget), { excludeComponents: outputTarget.excludeComponents || [], valueAccessorConfigs: outputTarget.valueAccessorConfigs || [], customElementsDir: (_a = outputTarget.customElementsDir) !== null && _a !== void 0 ? _a : 'components', outputType: (_b = outputTarget.outputType) !== null && _b !== void 0 ? _b : OutputTypes.Standalone });
1073
+ const results = {
1074
+ ...outputTarget,
1075
+ excludeComponents: outputTarget.excludeComponents || [],
1076
+ valueAccessorConfigs: outputTarget.valueAccessorConfigs || [],
1077
+ customElementsDir: outputTarget.customElementsDir ?? 'components',
1078
+ outputType: outputTarget.outputType ?? OutputTypes.Standalone,
1079
+ };
1051
1080
  if (config.rootDir == null) {
1052
1081
  throw new Error('rootDir is not set and it should be set by stencil itself');
1053
1082
  }
@@ -1,12 +1,13 @@
1
1
  import type { CompilerCtx, ComponentCompilerMeta, Config } from '@stencil/core/internal';
2
2
  import type { OutputTargetAngular, PackageJSON } from './types';
3
3
  export declare function angularDirectiveProxyOutput(compilerCtx: CompilerCtx, outputTarget: OutputTargetAngular, components: ComponentCompilerMeta[], config: Config): Promise<void>;
4
- export declare function generateProxies(components: ComponentCompilerMeta[], pkgData: PackageJSON, outputTarget: OutputTargetAngular, rootDir: string): string;
4
+ export declare function generateProxies(components: ComponentCompilerMeta[], pkgData: PackageJSON, outputTarget: OutputTargetAngular, rootDir: string, config: Config): string;
5
5
  /**
6
6
  * Generate a single component proxy file for ES modules output
7
7
  */
8
- export declare function generateComponentProxy(cmpMeta: ComponentCompilerMeta, pkgData: PackageJSON, outputTarget: OutputTargetAngular, rootDir: string): string;
8
+ export declare function generateComponentProxy(cmpMeta: ComponentCompilerMeta, pkgData: PackageJSON, outputTarget: OutputTargetAngular, rootDir: string, config: Config): string;
9
9
  /**
10
10
  * Generate a barrel file that re-exports all components
11
11
  */
12
12
  export declare function generateBarrelFile(components: ComponentCompilerMeta[], outputTarget: OutputTargetAngular): string;
13
+ export declare function getPathToComponentTypes(config: Config, outputTarget: OutputTargetAngular): string;
@@ -1,4 +1,7 @@
1
1
  import path from 'path';
2
+ import { fileURLToPath } from 'url';
3
+ const __filename = fileURLToPath(import.meta.url);
4
+ const __dirname = path.dirname(__filename);
2
5
  import { relativeImport, normalizePath, sortBy, readPackageJson, dashToPascalCase, createImportStatement, isOutputTypeCustomElementsBuild, OutputTypes, mapPropName, } from './utils';
3
6
  import { createAngularComponentDefinition, createComponentTypeDefinition } from './generate-angular-component';
4
7
  import { generateAngularDirectivesFile } from './generate-angular-directives-file';
@@ -21,7 +24,7 @@ export async function angularDirectiveProxyOutput(compilerCtx, outputTarget, com
21
24
  const proxiesDir = path.dirname(outputTarget.directivesProxyFile);
22
25
  for (const component of filteredComponents) {
23
26
  const componentFile = path.join(proxiesDir, `${component.tagName}.ts`);
24
- const componentText = generateComponentProxy(component, pkgData, outputTarget, rootDir);
27
+ const componentText = generateComponentProxy(component, pkgData, outputTarget, rootDir, config);
25
28
  tasks.push(compilerCtx.fs.writeFile(componentFile, componentText));
26
29
  }
27
30
  // Generate barrel file that re-exports all components
@@ -32,7 +35,7 @@ export async function angularDirectiveProxyOutput(compilerCtx, outputTarget, com
32
35
  }
33
36
  else {
34
37
  // Generate single file with all components (original behavior)
35
- const finalText = generateProxies(filteredComponents, pkgData, outputTarget, rootDir);
38
+ const finalText = generateProxies(filteredComponents, pkgData, outputTarget, rootDir, config);
36
39
  tasks.push(compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText));
37
40
  tasks.push(generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget));
38
41
  }
@@ -81,7 +84,7 @@ async function copyResources(config, outputTarget) {
81
84
  },
82
85
  ], srcDirectory);
83
86
  }
84
- export function generateProxies(components, pkgData, outputTarget, rootDir) {
87
+ export function generateProxies(components, pkgData, outputTarget, rootDir, config) {
85
88
  const distTypesDir = path.dirname(pkgData.types);
86
89
  const dtsFilePath = path.join(rootDir, distTypesDir, GENERATED_DTS);
87
90
  const { outputType } = outputTarget;
@@ -117,10 +120,9 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
117
120
  * otherwise we risk bundlers pulling in lazy loaded imports.
118
121
  */
119
122
  const generateTypeImports = () => {
120
- let importLocation = outputTarget.componentCorePackage
121
- ? normalizePath(outputTarget.componentCorePackage)
123
+ const importLocation = outputTarget.componentCorePackage
124
+ ? getPathToComponentTypes(config, outputTarget)
122
125
  : normalizePath(componentsTypeFile);
123
- importLocation += isCustomElementsBuild ? `/${outputTarget.customElementsDir}` : '';
124
126
  return `import ${isCustomElementsBuild ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
125
127
  };
126
128
  const typeImports = generateTypeImports();
@@ -141,13 +143,10 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
141
143
  const proxyFileOutput = [];
142
144
  const filterInternalProps = (prop) => !prop.internal;
143
145
  // Ensure that virtual properties has required as false.
144
- const mapInputProp = (prop) => {
145
- var _a;
146
- return ({
147
- name: prop.name,
148
- required: (_a = prop.required) !== null && _a !== void 0 ? _a : false,
149
- });
150
- };
146
+ const mapInputProp = (prop) => ({
147
+ name: prop.name,
148
+ required: prop.required ?? false,
149
+ });
151
150
  const { componentCorePackage, customElementsDir } = outputTarget;
152
151
  for (let cmpMeta of components) {
153
152
  const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
@@ -186,8 +185,7 @@ ${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n
186
185
  /**
187
186
  * Generate a single component proxy file for ES modules output
188
187
  */
189
- export function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir) {
190
- var _a;
188
+ export function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir, config) {
191
189
  const { outputType, componentCorePackage, customElementsDir } = outputTarget;
192
190
  const distTypesDir = path.dirname(pkgData.types);
193
191
  const dtsFilePath = path.join(rootDir, distTypesDir, GENERATED_DTS);
@@ -196,7 +194,7 @@ export function generateComponentProxy(cmpMeta, pkgData, outputTarget, rootDir)
196
194
  const isCustomElementsBuild = isOutputTypeCustomElementsBuild(outputType);
197
195
  const isStandaloneBuild = outputType === OutputTypes.Standalone;
198
196
  const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
199
- const hasOutputs = (_a = cmpMeta.events) === null || _a === void 0 ? void 0 : _a.some((event) => !event.internal);
197
+ const hasOutputs = cmpMeta.events?.some((event) => !event.internal);
200
198
  // Angular core imports for this component
201
199
  const angularCoreImports = ['ChangeDetectionStrategy', 'ChangeDetectorRef', 'Component', 'ElementRef', 'NgZone'];
202
200
  if (hasOutputs) {
@@ -211,8 +209,9 @@ ${createImportStatement(angularCoreImports, '@angular/core')}
211
209
 
212
210
  ${createImportStatement(['ProxyCmp'], './angular-component-lib/utils')}\n`;
213
211
  // Type imports
214
- let importLocation = componentCorePackage ? normalizePath(componentCorePackage) : normalizePath(componentsTypeFile);
215
- importLocation += isCustomElementsBuild ? `/${customElementsDir}` : '';
212
+ const importLocation = componentCorePackage
213
+ ? getPathToComponentTypes(config, outputTarget)
214
+ : normalizePath(componentsTypeFile);
216
215
  const typeImports = `import ${isCustomElementsBuild ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
217
216
  // defineCustomElement import
218
217
  let sourceImport = '';
@@ -221,13 +220,10 @@ ${createImportStatement(['ProxyCmp'], './angular-component-lib/utils')}\n`;
221
220
  }
222
221
  // Generate component definition
223
222
  const filterInternalProps = (prop) => !prop.internal;
224
- const mapInputProp = (prop) => {
225
- var _a;
226
- return ({
227
- name: prop.name,
228
- required: (_a = prop.required) !== null && _a !== void 0 ? _a : false,
229
- });
230
- };
223
+ const mapInputProp = (prop) => ({
224
+ name: prop.name,
225
+ required: prop.required ?? false,
226
+ });
231
227
  const internalProps = [];
232
228
  if (cmpMeta.properties) {
233
229
  internalProps.push(...cmpMeta.properties.filter(filterInternalProps));
@@ -273,5 +269,21 @@ export function generateBarrelFile(components, outputTarget) {
273
269
  .join('\n');
274
270
  return header + exports + '\n';
275
271
  }
272
+ export function getPathToComponentTypes(config, outputTarget) {
273
+ const basePkg = outputTarget.componentCorePackage || '';
274
+ // in v5, all types (including components.d.ts) are generated in the dist/types directory
275
+ const typesTarget = config.outputTargets?.find((o) => o.type === 'types');
276
+ if (typesTarget) {
277
+ const rawDir = typesTarget.dir || 'dist/types';
278
+ const relDir = config.rootDir && path.isAbsolute(rawDir) ? path.relative(config.rootDir, rawDir) : rawDir;
279
+ return normalizePath(path.join(basePkg, relDir, 'components'));
280
+ }
281
+ // v4: append customElementsDir for custom elements build, otherwise package root
282
+ const isCustomElementsBuild = isOutputTypeCustomElementsBuild(outputTarget.outputType);
283
+ if (isCustomElementsBuild && outputTarget.customElementsDir) {
284
+ return normalizePath(path.join(basePkg, outputTarget.customElementsDir));
285
+ }
286
+ return normalizePath(basePkg);
287
+ }
276
288
  const GENERATED_DTS = 'components.d.ts';
277
289
  const IMPORT_TYPES = 'Components';
package/dist/plugin.js CHANGED
@@ -17,8 +17,13 @@ export const angularOutputTarget = (outputTarget) => {
17
17
  };
18
18
  };
19
19
  export function normalizeOutputTarget(config, outputTarget) {
20
- var _a, _b;
21
- const results = Object.assign(Object.assign({}, outputTarget), { excludeComponents: outputTarget.excludeComponents || [], valueAccessorConfigs: outputTarget.valueAccessorConfigs || [], customElementsDir: (_a = outputTarget.customElementsDir) !== null && _a !== void 0 ? _a : 'components', outputType: (_b = outputTarget.outputType) !== null && _b !== void 0 ? _b : OutputTypes.Standalone });
20
+ const results = {
21
+ ...outputTarget,
22
+ excludeComponents: outputTarget.excludeComponents || [],
23
+ valueAccessorConfigs: outputTarget.valueAccessorConfigs || [],
24
+ customElementsDir: outputTarget.customElementsDir ?? 'components',
25
+ outputType: outputTarget.outputType ?? OutputTypes.Standalone,
26
+ };
22
27
  if (config.rootDir == null) {
23
28
  throw new Error('rootDir is not set and it should be set by stencil itself');
24
29
  }
package/dist/utils.js CHANGED
@@ -62,11 +62,10 @@ export function isRelativePath(path) {
62
62
  return path && path.startsWith('.');
63
63
  }
64
64
  export async function readPackageJson(config, rootDir) {
65
- var _a;
66
65
  const pkgJsonPath = path.join(rootDir, 'package.json');
67
66
  let pkgJson;
68
67
  try {
69
- pkgJson = (await ((_a = config.sys) === null || _a === void 0 ? void 0 : _a.readFile(pkgJsonPath, 'utf8')));
68
+ pkgJson = (await config.sys?.readFile(pkgJsonPath, 'utf8'));
70
69
  }
71
70
  catch (e) {
72
71
  throw new Error(`Missing "package.json" file for distribution: ${pkgJsonPath}`);
@@ -120,6 +119,14 @@ export const createComponentEventTypeImports = (componentTagName, events, option
120
119
  const namedImports = new Set();
121
120
  const isCustomElementsBuild = isOutputTypeCustomElementsBuild(options.outputType);
122
121
  const importPathName = normalizePath(componentCorePackage) + (isCustomElementsBuild ? `/${customElementsDir}` : '');
122
+ /**
123
+ * Each component's events are typed with the per-component CustomEvent type
124
+ * generated by Stencil (e.g. `MyComponentCustomEvent<Detail>`), which carries a
125
+ * narrowed `target`. Import it once per component that emits public events.
126
+ */
127
+ if (events.length > 0) {
128
+ imports.push(`import type { ${componentTagName}CustomEvent } from '${importPathName}';`);
129
+ }
123
130
  events.forEach((event) => {
124
131
  Object.entries(event.complexType.references).forEach(([typeName, refObject]) => {
125
132
  if (refObject.location === 'local' || refObject.location === 'import') {
@@ -0,0 +1,9 @@
1
+ import type { WizardContext } from '@stencil/cli';
2
+ export declare const wizard: {
3
+ init: {
4
+ id: string;
5
+ displayName: string;
6
+ description: string;
7
+ run({ config, workspaceRoot, prompts, nypm }: WizardContext): Promise<void>;
8
+ };
9
+ };
package/dist/wizard.js ADDED
@@ -0,0 +1,334 @@
1
+ import { Project, SyntaxKind } from 'ts-morph';
2
+ import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
3
+ import { join, relative, dirname } from 'node:path';
4
+ // ---------------------------------------------------------------------------
5
+ // ts-morph helpers
6
+ // ---------------------------------------------------------------------------
7
+ function amendStencilConfig(configPath, targetCode) {
8
+ const project = new Project({ skipAddingFilesFromTsConfig: true });
9
+ const src = project.addSourceFileAtPath(configPath);
10
+ if (!src.getImportDeclaration('@stencil/angular-output-target')) {
11
+ src.addImportDeclaration({
12
+ moduleSpecifier: '@stencil/angular-output-target',
13
+ namedImports: ['angularOutputTarget'],
14
+ });
15
+ }
16
+ const existingProp = src
17
+ .getDescendantsOfKind(SyntaxKind.PropertyAssignment)
18
+ .find((p) => p.getName() === 'outputTargets');
19
+ if (!existingProp) {
20
+ const configObj = src.getVariableDeclaration('config')?.getInitializerIfKind(SyntaxKind.ObjectLiteralExpression) ??
21
+ src
22
+ .getDescendantsOfKind(SyntaxKind.ObjectLiteralExpression)
23
+ .find((obj) => obj.getProperty('namespace') !== undefined);
24
+ if (!configObj)
25
+ throw new Error('Could not find Stencil config object in stencil.config.ts');
26
+ configObj.addPropertyAssignment({ name: 'outputTargets', initializer: '[]' });
27
+ }
28
+ const prop = src.getDescendantsOfKind(SyntaxKind.PropertyAssignment).find((p) => p.getName() === 'outputTargets');
29
+ const arr = prop.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression);
30
+ if (!arr)
31
+ throw new Error('outputTargets is not an array literal in stencil.config.ts');
32
+ const existing = arr.getText();
33
+ const alreadyAdded = existing.includes('angularOutputTarget(');
34
+ // Filter out any existing angularOutputTarget call so it can be replaced cleanly
35
+ const elements = arr
36
+ .getElements()
37
+ .map((e) => e.getText().trim())
38
+ .filter((e) => !e.includes('angularOutputTarget('));
39
+ const hasStandalone = existing.includes("type: 'standalone'") ||
40
+ existing.includes('type: "standalone"') ||
41
+ existing.includes("type: 'dist-custom-elements'") ||
42
+ existing.includes('type: "dist-custom-elements"');
43
+ if (!hasStandalone)
44
+ elements.push("{ type: 'standalone' }");
45
+ elements.push(targetCode);
46
+ prop.setInitializer(`[\n${elements.map((e) => ` ${e}`).join(',\n')},\n]`);
47
+ src.formatText();
48
+ src.saveSync();
49
+ return !alreadyAdded;
50
+ }
51
+ // ---------------------------------------------------------------------------
52
+ // pnpm build approval
53
+ // ---------------------------------------------------------------------------
54
+ async function allowPnpmBuild(pkg, rootDir, workspaceRoot) {
55
+ const base = workspaceRoot ?? rootDir;
56
+ const workspaceYaml = join(base, 'pnpm-workspace.yaml');
57
+ if (await pathExists(workspaceYaml)) {
58
+ let content = await readFile(workspaceYaml, 'utf8');
59
+ // onlyBuiltDependencies (list form)
60
+ if (!content.includes(`- '${pkg}'`) && !content.includes(`- "${pkg}"`) && !content.includes(`- ${pkg}\n`)) {
61
+ if (content.includes('onlyBuiltDependencies:')) {
62
+ content = content.replace(/^(onlyBuiltDependencies:[ \t]*\n)/m, `$1 - ${pkg}\n`);
63
+ }
64
+ else {
65
+ content += `\nonlyBuiltDependencies:\n - ${pkg}\n`;
66
+ }
67
+ }
68
+ // allowBuilds (map form, pnpm 11+)
69
+ if (!content.includes(`${pkg}: true`)) {
70
+ if (content.includes('allowBuilds:')) {
71
+ content = content.replace(/^(allowBuilds:[ \t]*\n)/m, `$1 ${pkg}: true\n`);
72
+ }
73
+ else {
74
+ content += `\nallowBuilds:\n ${pkg}: true\n`;
75
+ }
76
+ }
77
+ await writeFile(workspaceYaml, content, 'utf8');
78
+ return;
79
+ }
80
+ // Non-workspace: amend root package.json pnpm field
81
+ const rootPkgPath = join(base, 'package.json');
82
+ try {
83
+ const rootPkg = JSON.parse(await readFile(rootPkgPath, 'utf8'));
84
+ const existingList = rootPkg?.pnpm?.onlyBuiltDependencies ?? [];
85
+ const existingMap = rootPkg?.pnpm?.allowBuilds ?? {};
86
+ rootPkg.pnpm = {
87
+ ...rootPkg.pnpm,
88
+ onlyBuiltDependencies: existingList.includes(pkg) ? existingList : [...existingList, pkg],
89
+ allowBuilds: { ...existingMap, [pkg]: true },
90
+ };
91
+ await writeFile(rootPkgPath, JSON.stringify(rootPkg, null, 2) + '\n', 'utf8');
92
+ }
93
+ catch {
94
+ // no root package.json — nothing to amend
95
+ }
96
+ }
97
+ // ---------------------------------------------------------------------------
98
+ // Wrapper package scaffolding
99
+ // ---------------------------------------------------------------------------
100
+ async function pathExists(p) {
101
+ try {
102
+ await access(p);
103
+ return true;
104
+ }
105
+ catch {
106
+ return false;
107
+ }
108
+ }
109
+ function toPascalCase(s) {
110
+ return s
111
+ .split(/[-_]/)
112
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
113
+ .join('');
114
+ }
115
+ async function scaffoldWrapperPackage(wrapperDir, packageName, corePackageName, corePkgVersion, namespace, outputType) {
116
+ const libDir = join(wrapperDir, 'src', 'lib');
117
+ await mkdir(libDir, { recursive: true });
118
+ const className = toPascalCase(namespace);
119
+ const pkgJson = {
120
+ name: packageName,
121
+ version: '0.0.1',
122
+ scripts: { build: 'ng-packagr -p ng-package.json' },
123
+ peerDependencies: {
124
+ '@angular/common': '>=19',
125
+ '@angular/core': '>=19',
126
+ },
127
+ dependencies: { [corePackageName]: corePkgVersion },
128
+ devDependencies: {
129
+ '@angular/common': '>=19',
130
+ '@angular/core': '>=19',
131
+ '@angular/compiler': '>=19',
132
+ '@angular/compiler-cli': '>=19',
133
+ 'ng-packagr': '>=19',
134
+ rxjs: '>=6',
135
+ tslib: '>=2',
136
+ typescript: '>=4 <7',
137
+ },
138
+ };
139
+ const ngPackage = {
140
+ lib: { entryFile: 'src/index.ts' },
141
+ deleteDestPath: false,
142
+ allowedNonPeerDependencies: [corePackageName],
143
+ };
144
+ const tsConfig = {
145
+ compilerOptions: {
146
+ target: 'ES2022',
147
+ module: 'ES2022',
148
+ moduleResolution: 'bundler',
149
+ experimentalDecorators: true,
150
+ strict: true,
151
+ skipLibCheck: true,
152
+ },
153
+ include: ['src/**/*.ts'],
154
+ };
155
+ const tsConfigLib = {
156
+ extends: './tsconfig.json',
157
+ compilerOptions: { declaration: false, inlineSources: true },
158
+ angularCompilerOptions: { compilationMode: 'partial' },
159
+ };
160
+ // NgModule file — only needed for 'component' outputType
161
+ let ngModuleTs;
162
+ if (outputType === 'component') {
163
+ ngModuleTs =
164
+ `import { NgModule } from '@angular/core';\n` +
165
+ `import { DIRECTIVES } from './directives.array';\n\n` +
166
+ `@NgModule({\n` +
167
+ ` declarations: [...DIRECTIVES],\n` +
168
+ ` exports: [...DIRECTIVES],\n` +
169
+ `})\n` +
170
+ `export class ${className}Module {}\n`;
171
+ }
172
+ // src/index.ts — public API surface
173
+ // directives.ts (the directivesProxyFile) sits at src/lib/directives.ts
174
+ let indexTs = `// Public API — re-exports generated by @stencil/angular-output-target.\n`;
175
+ indexTs += `export * from './lib/directives';\n`;
176
+ if (outputType === 'component') {
177
+ indexTs += `export * from './lib/${namespace}.module';\n`;
178
+ }
179
+ await Promise.all([
180
+ writeFile(join(wrapperDir, '.gitignore'), 'dist/\nnode_modules/\n.angular/\n', 'utf8'),
181
+ writeFile(join(wrapperDir, 'package.json'), JSON.stringify(pkgJson, null, 2) + '\n', 'utf8'),
182
+ writeFile(join(wrapperDir, 'ng-package.json'), JSON.stringify(ngPackage, null, 2) + '\n', 'utf8'),
183
+ writeFile(join(wrapperDir, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2) + '\n', 'utf8'),
184
+ writeFile(join(wrapperDir, 'tsconfig.lib.json'), JSON.stringify(tsConfigLib, null, 2) + '\n', 'utf8'),
185
+ writeFile(join(wrapperDir, 'src', 'index.ts'), indexTs, 'utf8'),
186
+ ...(ngModuleTs ? [writeFile(join(libDir, `${namespace}.module.ts`), ngModuleTs, 'utf8')] : []),
187
+ ]);
188
+ }
189
+ // ---------------------------------------------------------------------------
190
+ // Wizard
191
+ // ---------------------------------------------------------------------------
192
+ export const wizard = {
193
+ init: {
194
+ id: '@stencil/angular-output-target',
195
+ displayName: 'Angular',
196
+ description: 'Angular component wrappers for your Stencil components',
197
+ async run({ config, workspaceRoot, prompts, nypm }) {
198
+ const { intro, outro, text, select, confirm, spinner, isCancel, cancel, log } = prompts;
199
+ intro('Angular output target');
200
+ const stencilConfigPath = join(config.rootDir, 'stencil.config.ts');
201
+ // Guard: already configured?
202
+ const guardProject = new Project({ skipAddingFilesFromTsConfig: true });
203
+ const alreadyConfigured = guardProject
204
+ .addSourceFileAtPath(stencilConfigPath)
205
+ .getDescendantsOfKind(SyntaxKind.PropertyAssignment)
206
+ .find((p) => p.getName() === 'outputTargets')
207
+ ?.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression)
208
+ ?.getText()
209
+ .includes('angularOutputTarget(');
210
+ if (alreadyConfigured) {
211
+ const redo = await confirm({
212
+ message: 'angularOutputTarget is already in stencil.config.ts — reconfigure?',
213
+ initialValue: false,
214
+ });
215
+ if (isCancel(redo) || !redo) {
216
+ cancel('Skipping Angular setup.');
217
+ return;
218
+ }
219
+ }
220
+ // Where should the wrapper package live?
221
+ let wrapperDir;
222
+ const defaultName = `${config.fsNamespace}-angular`;
223
+ if (workspaceRoot) {
224
+ const rawName = await text({
225
+ message: 'Wrapper package name?',
226
+ placeholder: defaultName,
227
+ defaultValue: defaultName,
228
+ });
229
+ if (isCancel(rawName)) {
230
+ cancel('Setup cancelled.');
231
+ return;
232
+ }
233
+ wrapperDir = join(dirname(config.rootDir), rawName);
234
+ }
235
+ else {
236
+ const defaultRel = `../${config.fsNamespace}-angular`;
237
+ const rawDir = await text({
238
+ message: 'Wrapper package directory? (relative to stencil.config.ts)',
239
+ placeholder: defaultRel,
240
+ defaultValue: defaultRel,
241
+ });
242
+ if (isCancel(rawDir)) {
243
+ cancel('Setup cancelled.');
244
+ return;
245
+ }
246
+ wrapperDir = join(config.rootDir, rawDir);
247
+ }
248
+ // Angular component type — drives what we scaffold
249
+ const outputType = await select({
250
+ message: 'Angular component type?',
251
+ options: [
252
+ { value: 'standalone', label: 'Standalone', hint: 'Modern Angular (v19+) — no NgModule' },
253
+ { value: 'component', label: 'NgModule', hint: 'Classic — all components in one module' },
254
+ { value: 'scam', label: 'SCAM', hint: 'One NgModule per component' },
255
+ ],
256
+ });
257
+ if (isCancel(outputType)) {
258
+ cancel('Setup cancelled.');
259
+ return;
260
+ }
261
+ const selectedOutputType = outputType;
262
+ // Derive stencil.config-relative paths from the wrapper location
263
+ const directivesProxyFile = relative(config.rootDir, join(wrapperDir, 'src', 'lib', 'directives.ts')).replace(/\\/g, '/');
264
+ const directivesArrayFile = selectedOutputType === 'component'
265
+ ? relative(config.rootDir, join(wrapperDir, 'src', 'lib', 'directives.array.ts')).replace(/\\/g, '/')
266
+ : undefined;
267
+ const componentCorePackage = config.fsNamespace;
268
+ // Scaffold wrapper package if the directory doesn't exist yet
269
+ const shouldScaffold = !(await pathExists(wrapperDir));
270
+ if (shouldScaffold) {
271
+ const s = spinner();
272
+ s.start(`Scaffolding wrapper package at ${relative(workspaceRoot ?? config.rootDir, wrapperDir)}`);
273
+ try {
274
+ const corePkgVersion = workspaceRoot ? 'workspace:*' : `file:${relative(wrapperDir, config.rootDir)}`;
275
+ await scaffoldWrapperPackage(wrapperDir, `${config.fsNamespace}-angular`, componentCorePackage, corePkgVersion, config.fsNamespace, selectedOutputType);
276
+ s.stop('Wrapper package scaffolded');
277
+ }
278
+ catch (e) {
279
+ s.stop('Scaffolding failed — continuing');
280
+ log.warn(`Could not scaffold wrapper package: ${e}`);
281
+ }
282
+ }
283
+ // Ensure esbuild (used by ng-packagr) is allowed to run install scripts in pnpm workspaces
284
+ try {
285
+ await allowPnpmBuild('esbuild', config.rootDir, workspaceRoot);
286
+ }
287
+ catch {
288
+ // non-fatal — user can run `pnpm approve-builds` manually
289
+ }
290
+ // Derive customElementsDir from an existing standalone target, or use the v5 default
291
+ const standaloneTarget = config.outputTargets?.find((o) => o.type === 'standalone');
292
+ const customElementsDir = standaloneTarget?.dir
293
+ ? relative(config.rootDir, standaloneTarget.dir).replace(/\\/g, '/')
294
+ : 'dist/standalone';
295
+ // Build target config
296
+ const lines = [
297
+ `componentCorePackage: '${componentCorePackage}'`,
298
+ `customElementsDir: '${customElementsDir}'`,
299
+ `directivesProxyFile: '${directivesProxyFile}'`,
300
+ ...(directivesArrayFile ? [`directivesArrayFile: '${directivesArrayFile}'`] : []),
301
+ ...(selectedOutputType !== 'standalone' ? [`outputType: '${selectedOutputType}'`] : []),
302
+ ];
303
+ const targetCode = `angularOutputTarget({\n ${lines.join(',\n ')},\n})`;
304
+ // Amend stencil.config.ts in one pass so formatText() sees the final array
305
+ try {
306
+ const added = amendStencilConfig(stencilConfigPath, targetCode);
307
+ log.success(added ? 'stencil.config.ts updated' : 'angularOutputTarget already present — no changes made');
308
+ }
309
+ catch (e) {
310
+ log.warn(`Could not automatically update stencil.config.ts (${e}). Add manually:\n\n` +
311
+ `import { angularOutputTarget } from '@stencil/angular-output-target';\n` +
312
+ `// in outputTargets:\n${targetCode}`);
313
+ }
314
+ // Install @stencil/angular-output-target as a devDep of the core package
315
+ log.info('Installing @stencil/angular-output-target...');
316
+ await nypm.addDependency(['@stencil/angular-output-target'], { cwd: config.rootDir, dev: true });
317
+ log.success('Installed @stencil/angular-output-target');
318
+ if (shouldScaffold && (await pathExists(wrapperDir))) {
319
+ log.info('Installing Angular dependencies in wrapper package...');
320
+ await nypm.addDependency([
321
+ '@angular/core',
322
+ '@angular/common',
323
+ '@angular/compiler',
324
+ '@angular/compiler-cli',
325
+ 'ng-packagr',
326
+ 'rxjs',
327
+ 'tslib',
328
+ ], { cwd: wrapperDir, dev: true });
329
+ log.success('Angular dependencies installed');
330
+ }
331
+ outro('Angular output target configured');
332
+ },
333
+ },
334
+ };
package/package.json CHANGED
@@ -1,14 +1,22 @@
1
1
  {
2
2
  "name": "@stencil/angular-output-target",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "Angular output target for @stencil/core components.",
5
- "main": "dist/index.cjs.js",
5
+ "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
7
+ "type": "module",
8
+ "stencil": {
9
+ "wizard": "./dist/wizard.js"
10
+ },
7
11
  "exports": {
8
12
  ".": {
9
13
  "types": "./dist/index.d.ts",
10
14
  "import": "./dist/index.js",
11
- "require": "./dist/index.cjs.js"
15
+ "require": "./dist/index.cjs"
16
+ },
17
+ "./wizard": {
18
+ "types": "./dist/wizard.d.ts",
19
+ "import": "./dist/wizard.js"
12
20
  }
13
21
  },
14
22
  "types": "dist/index.d.ts",
@@ -47,7 +55,11 @@
47
55
  "bugs": {
48
56
  "url": "https://github.com/stenciljs/output-targets/issues"
49
57
  },
58
+ "dependencies": {
59
+ "ts-morph": "^28.0.0"
60
+ },
50
61
  "devDependencies": {
62
+ "@stencil/cli": "^5.0.0 || >=5.0.0-alpha.0 <5.0.0-next.0",
51
63
  "@angular/core": "8.2.14",
52
64
  "@angular/forms": "8.2.14",
53
65
  "@stencil/core": "4.41.0",
@@ -59,7 +71,7 @@
59
71
  "vitest": "^2.1.4"
60
72
  },
61
73
  "peerDependencies": {
62
- "@stencil/core": ">=2.0.0 || >=3 || >= 4.0.0-beta.0 || >= 4.0.0"
74
+ "@stencil/core": ">=2.0.0 || >=3 || >=4.0.0 || ^5.0.0 || >=5.0.0-alpha.0 <5.0.0-next.0"
63
75
  },
64
76
  "gitHead": "a3588e905186a0e86e7f88418fd5b2f9531b55e0"
65
77
  }