@stencil/angular-output-target 0.2.0 → 0.4.0-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.
@@ -32,13 +32,30 @@ export const proxyOutputs = (instance: any, el: any, events: string[]) => {
32
32
  events.forEach(eventName => instance[eventName] = fromEvent(el, eventName));
33
33
  }
34
34
 
35
- export function ProxyCmp(opts: { inputs?: any; methods?: any }) {
36
- const decorator = function(cls: any) {
37
- if (opts.inputs) {
38
- proxyInputs(cls, opts.inputs);
35
+ export const defineCustomElement = (tagName: string, customElement: any) => {
36
+ if (
37
+ customElement !== undefined &&
38
+ typeof customElements !== 'undefined' &&
39
+ !customElements.get(tagName)
40
+ ) {
41
+ customElements.define(tagName, customElement);
42
+ }
43
+ }
44
+
45
+ // tslint:disable-next-line: only-arrow-functions
46
+ export function ProxyCmp(opts: { tagName: string, defineCustomElementFn?: () => void, inputs?: any; methods?: any }) {
47
+ const decorator = function (cls: any) {
48
+ const { tagName, customElement, inputs, methods } = opts;
49
+
50
+ if (defineCustomElementFn !== undefined) {
51
+ defineCustomElementFn();
52
+ }
53
+
54
+ if (inputs) {
55
+ proxyInputs(cls, inputs);
39
56
  }
40
- if (opts.methods) {
41
- proxyMethods(cls, opts.methods);
57
+ if (methods) {
58
+ proxyMethods(cls, methods);
42
59
  }
43
60
  return cls;
44
61
  };
@@ -1,2 +1,2 @@
1
1
  import type { ComponentCompilerMeta } from '@stencil/core/internal';
2
- export declare const createComponentDefinition: (componentCorePackage: string, distTypesDir: string, rootDir: string) => (cmpMeta: ComponentCompilerMeta) => string;
2
+ export declare const createComponentDefinition: (componentCorePackage: string, distTypesDir: string, rootDir: string, includeImportCustomElements?: boolean, customElementsDir?: string) => (cmpMeta: ComponentCompilerMeta) => string;
@@ -1,5 +1,5 @@
1
- import { dashToPascalCase } from './utils';
2
- export const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir) => (cmpMeta) => {
1
+ import { dashToPascalCase, normalizePath } from './utils';
2
+ export const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir, includeImportCustomElements = false, customElementsDir = 'components') => (cmpMeta) => {
3
3
  // Collect component meta
4
4
  const inputs = [
5
5
  ...cmpMeta.properties.filter((prop) => !prop.internal).map((prop) => prop.name),
@@ -18,9 +18,6 @@ export const createComponentDefinition = (componentCorePackage, distTypesDir, ro
18
18
  if (inputs.length > 0) {
19
19
  directiveOpts.push(`inputs: ['${inputs.join(`', '`)}']`);
20
20
  }
21
- if (outputs.length > 0) {
22
- directiveOpts.push(`outputs: ['${outputs.map((output) => output.name).join(`', '`)}']`);
23
- }
24
21
  const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
25
22
  const outputsInterface = new Set();
26
23
  const outputReferenceRemap = {};
@@ -32,23 +29,23 @@ export const createComponentDefinition = (componentCorePackage, distTypesDir, ro
32
29
  const remappedReference = `I${cmpMeta.componentClassName}${reference}`;
33
30
  if (refObject.location === 'local' || refObject.location === 'import') {
34
31
  outputReferenceRemap[reference] = remappedReference;
35
- outputsInterface.add(`import { ${reference} as ${remappedReference} } from '${componentCorePackage}';`);
32
+ let importLocation = componentCorePackage;
33
+ if (componentCorePackage !== undefined) {
34
+ const dirPath = includeImportCustomElements ? `/${customElementsDir || 'components'}` : '';
35
+ importLocation = `${normalizePath(componentCorePackage)}${dirPath}`;
36
+ }
37
+ outputsInterface.add(`import type { ${reference} as ${remappedReference} } from '${importLocation}';`);
36
38
  }
37
39
  });
38
40
  });
39
- const lines = [
40
- '',
41
- `${[...outputsInterface].join('\n')}
42
- export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {}
43
- ${getProxyCmp(inputs, methods)}
44
- @Component({
45
- ${directiveOpts.join(',\n ')}
46
- })
47
- export class ${tagNameAsPascal} {`,
41
+ const componentEvents = [
42
+ '' // Empty first line
48
43
  ];
49
44
  // Generate outputs
50
- outputs.forEach((output) => {
51
- lines.push(` /** ${output.docs.text} ${output.docs.tags.map((tag) => `@${tag.name} ${tag.text}`)}*/`);
45
+ outputs.forEach((output, index) => {
46
+ componentEvents.push(` /**
47
+ * ${output.docs.text} ${output.docs.tags.map((tag) => `@${tag.name} ${tag.text}`)}
48
+ */`);
52
49
  /**
53
50
  * The original attribute contains the original type defined by the devs.
54
51
  * This regexp normalizes the reference, by removing linebreaks,
@@ -62,8 +59,23 @@ export class ${tagNameAsPascal} {`,
62
59
  .replace(/\n/g, ' ')
63
60
  .replace(/\s{2,}/g, ' ')
64
61
  .replace(/,\s*/g, ', '));
65
- lines.push(` ${output.name}!: EventEmitter<CustomEvent<${outputTypeRemapped.trim()}>>;`);
62
+ componentEvents.push(` ${output.name}: EventEmitter<CustomEvent<${outputTypeRemapped.trim()}>>;`);
63
+ if (index === outputs.length - 1) {
64
+ // Empty line to push end `}` to new line
65
+ componentEvents.push('\n');
66
+ }
66
67
  });
68
+ const lines = [
69
+ '',
70
+ `${[...outputsInterface].join('\n')}
71
+ export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {${componentEvents.length > 1 ? componentEvents.join('\n') : ''}}
72
+
73
+ ${getProxyCmp(cmpMeta.tagName, includeImportCustomElements, inputs, methods)}
74
+ @Component({
75
+ ${directiveOpts.join(',\n ')}
76
+ })
77
+ export class ${tagNameAsPascal} {`,
78
+ ];
67
79
  lines.push(' protected el: HTMLElement;');
68
80
  lines.push(` constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
69
81
  c.detach();
@@ -75,13 +87,13 @@ export class ${tagNameAsPascal} {`,
75
87
  lines.push(`}`);
76
88
  return lines.join('\n');
77
89
  };
78
- function getProxyCmp(inputs, methods) {
90
+ function getProxyCmp(tagName, includeCustomElement, inputs, methods) {
79
91
  const hasInputs = inputs.length > 0;
80
92
  const hasMethods = methods.length > 0;
81
- const proxMeta = [];
82
- if (!hasInputs && !hasMethods) {
83
- return '';
84
- }
93
+ const proxMeta = [
94
+ `tagName: \'${tagName}\'`,
95
+ `customElement: ${includeCustomElement ? 'define' + dashToPascalCase(tagName) : 'undefined'}`
96
+ ];
85
97
  if (hasInputs)
86
98
  proxMeta.push(`inputs: ['${inputs.join(`', '`)}']`);
87
99
  if (hasMethods)
package/dist/index.cjs.js CHANGED
@@ -3,16 +3,12 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var path = require('path');
6
- var util = require('util');
7
- var fs = require('fs');
8
6
  var os = require('os');
9
7
 
10
8
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
11
9
 
12
10
  var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
13
- var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
14
11
 
15
- const readFile = util.promisify(fs__default['default'].readFile);
16
12
  const toLowerCase = (str) => str.toLowerCase();
17
13
  const dashToPascalCase = (str) => toLowerCase(str)
18
14
  .split('-')
@@ -66,11 +62,12 @@ function relativeImport(pathFrom, pathTo, ext) {
66
62
  }
67
63
  return normalizePath(`${relativePath}/${path__default['default'].basename(pathTo, ext)}`);
68
64
  }
69
- async function readPackageJson(rootDir) {
65
+ async function readPackageJson(config, rootDir) {
66
+ var _a;
70
67
  const pkgJsonPath = path__default['default'].join(rootDir, 'package.json');
71
68
  let pkgJson;
72
69
  try {
73
- pkgJson = await readFile(pkgJsonPath, 'utf8');
70
+ pkgJson = (await ((_a = config.sys) === null || _a === void 0 ? void 0 : _a.readFile(pkgJsonPath, 'utf8')));
74
71
  }
75
72
  catch (e) {
76
73
  throw new Error(`Missing "package.json" file for distribution: ${pkgJsonPath}`);
@@ -88,7 +85,7 @@ const EXTENDED_PATH_REGEX = /^\\\\\?\\/;
88
85
  const NON_ASCII_REGEX = /[^\x00-\x80]+/;
89
86
  const SLASH_REGEX = /\\/g;
90
87
 
91
- const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir) => (cmpMeta) => {
88
+ const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir, includeImportCustomElements = false, customElementsDir = 'components') => (cmpMeta) => {
92
89
  // Collect component meta
93
90
  const inputs = [
94
91
  ...cmpMeta.properties.filter((prop) => !prop.internal).map((prop) => prop.name),
@@ -107,9 +104,6 @@ const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir)
107
104
  if (inputs.length > 0) {
108
105
  directiveOpts.push(`inputs: ['${inputs.join(`', '`)}']`);
109
106
  }
110
- if (outputs.length > 0) {
111
- directiveOpts.push(`outputs: ['${outputs.map((output) => output.name).join(`', '`)}']`);
112
- }
113
107
  const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
114
108
  const outputsInterface = new Set();
115
109
  const outputReferenceRemap = {};
@@ -121,23 +115,23 @@ const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir)
121
115
  const remappedReference = `I${cmpMeta.componentClassName}${reference}`;
122
116
  if (refObject.location === 'local' || refObject.location === 'import') {
123
117
  outputReferenceRemap[reference] = remappedReference;
124
- outputsInterface.add(`import { ${reference} as ${remappedReference} } from '${componentCorePackage}';`);
118
+ let importLocation = componentCorePackage;
119
+ if (componentCorePackage !== undefined) {
120
+ const dirPath = includeImportCustomElements ? `/${customElementsDir || 'components'}` : '';
121
+ importLocation = `${normalizePath(componentCorePackage)}${dirPath}`;
122
+ }
123
+ outputsInterface.add(`import type { ${reference} as ${remappedReference} } from '${importLocation}';`);
125
124
  }
126
125
  });
127
126
  });
128
- const lines = [
129
- '',
130
- `${[...outputsInterface].join('\n')}
131
- export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {}
132
- ${getProxyCmp(inputs, methods)}
133
- @Component({
134
- ${directiveOpts.join(',\n ')}
135
- })
136
- export class ${tagNameAsPascal} {`,
127
+ const componentEvents = [
128
+ '' // Empty first line
137
129
  ];
138
130
  // Generate outputs
139
- outputs.forEach((output) => {
140
- lines.push(` /** ${output.docs.text} ${output.docs.tags.map((tag) => `@${tag.name} ${tag.text}`)}*/`);
131
+ outputs.forEach((output, index) => {
132
+ componentEvents.push(` /**
133
+ * ${output.docs.text} ${output.docs.tags.map((tag) => `@${tag.name} ${tag.text}`)}
134
+ */`);
141
135
  /**
142
136
  * The original attribute contains the original type defined by the devs.
143
137
  * This regexp normalizes the reference, by removing linebreaks,
@@ -151,8 +145,23 @@ export class ${tagNameAsPascal} {`,
151
145
  .replace(/\n/g, ' ')
152
146
  .replace(/\s{2,}/g, ' ')
153
147
  .replace(/,\s*/g, ', '));
154
- lines.push(` ${output.name}!: EventEmitter<CustomEvent<${outputTypeRemapped.trim()}>>;`);
148
+ componentEvents.push(` ${output.name}: EventEmitter<CustomEvent<${outputTypeRemapped.trim()}>>;`);
149
+ if (index === outputs.length - 1) {
150
+ // Empty line to push end `}` to new line
151
+ componentEvents.push('\n');
152
+ }
155
153
  });
154
+ const lines = [
155
+ '',
156
+ `${[...outputsInterface].join('\n')}
157
+ export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {${componentEvents.length > 1 ? componentEvents.join('\n') : ''}}
158
+
159
+ ${getProxyCmp(cmpMeta.tagName, includeImportCustomElements, inputs, methods)}
160
+ @Component({
161
+ ${directiveOpts.join(',\n ')}
162
+ })
163
+ export class ${tagNameAsPascal} {`,
164
+ ];
156
165
  lines.push(' protected el: HTMLElement;');
157
166
  lines.push(` constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
158
167
  c.detach();
@@ -164,13 +173,13 @@ export class ${tagNameAsPascal} {`,
164
173
  lines.push(`}`);
165
174
  return lines.join('\n');
166
175
  };
167
- function getProxyCmp(inputs, methods) {
176
+ function getProxyCmp(tagName, includeCustomElement, inputs, methods) {
168
177
  const hasInputs = inputs.length > 0;
169
178
  const hasMethods = methods.length > 0;
170
- const proxMeta = [];
171
- if (!hasInputs && !hasMethods) {
172
- return '';
173
- }
179
+ const proxMeta = [
180
+ `tagName: \'${tagName}\'`,
181
+ `customElement: ${includeCustomElement ? 'define' + dashToPascalCase(tagName) : 'undefined'}`
182
+ ];
174
183
  if (hasInputs)
175
184
  proxMeta.push(`inputs: ['${inputs.join(`', '`)}']`);
176
185
  if (hasMethods)
@@ -259,7 +268,7 @@ const VALUE_ACCESSOR_EVENTTARGETS = ` '(<VALUE_ACCESSOR_EVENT>)': 'handleChan
259
268
  async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
260
269
  const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
261
270
  const rootDir = config.rootDir;
262
- const pkgData = await readPackageJson(rootDir);
271
+ const pkgData = await readPackageJson(config, rootDir);
263
272
  const finalText = generateProxies(filteredComponents, pkgData, outputTarget, config.rootDir);
264
273
  await Promise.all([
265
274
  compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText),
@@ -294,14 +303,39 @@ function generateProxies(components, pkgData, outputTarget, rootDir) {
294
303
  /* auto-generated angular directive proxies */
295
304
  import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, NgZone } from '@angular/core';
296
305
  import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';\n`;
297
- const typeImports = !outputTarget.componentCorePackage
298
- ? `import { ${IMPORT_TYPES} } from '${normalizePath(componentsTypeFile)}';`
299
- : `import { ${IMPORT_TYPES} } from '${normalizePath(outputTarget.componentCorePackage)}';`;
306
+ /**
307
+ * Generate JSX import type from correct location.
308
+ * When using custom elements build, we need to import from
309
+ * either the "components" directory or customElementsDir
310
+ * otherwise we risk bundlers pulling in lazy loaded imports.
311
+ */
312
+ const generateTypeImports = () => {
313
+ let importLocation = outputTarget.componentCorePackage ? normalizePath(outputTarget.componentCorePackage) : normalizePath(componentsTypeFile);
314
+ importLocation += outputTarget.includeImportCustomElements ? `/${outputTarget.customElementsDir || 'components'}` : '';
315
+ return `import ${outputTarget.includeImportCustomElements ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
316
+ };
317
+ const typeImports = generateTypeImports();
318
+ let sourceImports = '';
319
+ /**
320
+ * Build an array of Custom Elements build imports and namespace them
321
+ * so that they do not conflict with the React wrapper names. For example,
322
+ * IonButton would be imported as IonButtonCmp so as to not conflict with the
323
+ * IonButton React Component that takes in the Web Component as a parameter.
324
+ */
325
+ if (outputTarget.includeImportCustomElements && outputTarget.componentCorePackage !== undefined) {
326
+ const cmpImports = components.map(component => {
327
+ const pascalImport = dashToPascalCase(component.tagName);
328
+ return `import { defineCustomElement as define${pascalImport} } from '${normalizePath(outputTarget.componentCorePackage)}/${outputTarget.customElementsDir ||
329
+ 'components'}/${component.tagName}.js';`;
330
+ });
331
+ sourceImports = cmpImports.join('\n');
332
+ }
300
333
  const final = [
301
334
  imports,
302
335
  typeImports,
336
+ sourceImports,
303
337
  components
304
- .map(createComponentDefinition(outputTarget.componentCorePackage))
338
+ .map(createComponentDefinition(outputTarget.componentCorePackage, distTypesDir, rootDir, outputTarget.includeImportCustomElements, outputTarget.customElementsDir))
305
339
  .join('\n'),
306
340
  ];
307
341
  return final.join('\n') + '\n';
package/dist/index.js CHANGED
@@ -1,9 +1,6 @@
1
1
  import path from 'path';
2
- import { promisify } from 'util';
3
- import fs from 'fs';
4
2
  import { EOL } from 'os';
5
3
 
6
- const readFile = promisify(fs.readFile);
7
4
  const toLowerCase = (str) => str.toLowerCase();
8
5
  const dashToPascalCase = (str) => toLowerCase(str)
9
6
  .split('-')
@@ -57,11 +54,12 @@ function relativeImport(pathFrom, pathTo, ext) {
57
54
  }
58
55
  return normalizePath(`${relativePath}/${path.basename(pathTo, ext)}`);
59
56
  }
60
- async function readPackageJson(rootDir) {
57
+ async function readPackageJson(config, rootDir) {
58
+ var _a;
61
59
  const pkgJsonPath = path.join(rootDir, 'package.json');
62
60
  let pkgJson;
63
61
  try {
64
- pkgJson = await readFile(pkgJsonPath, 'utf8');
62
+ pkgJson = (await ((_a = config.sys) === null || _a === void 0 ? void 0 : _a.readFile(pkgJsonPath, 'utf8')));
65
63
  }
66
64
  catch (e) {
67
65
  throw new Error(`Missing "package.json" file for distribution: ${pkgJsonPath}`);
@@ -79,7 +77,7 @@ const EXTENDED_PATH_REGEX = /^\\\\\?\\/;
79
77
  const NON_ASCII_REGEX = /[^\x00-\x80]+/;
80
78
  const SLASH_REGEX = /\\/g;
81
79
 
82
- const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir) => (cmpMeta) => {
80
+ const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir, includeImportCustomElements = false, customElementsDir = 'components') => (cmpMeta) => {
83
81
  // Collect component meta
84
82
  const inputs = [
85
83
  ...cmpMeta.properties.filter((prop) => !prop.internal).map((prop) => prop.name),
@@ -98,9 +96,6 @@ const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir)
98
96
  if (inputs.length > 0) {
99
97
  directiveOpts.push(`inputs: ['${inputs.join(`', '`)}']`);
100
98
  }
101
- if (outputs.length > 0) {
102
- directiveOpts.push(`outputs: ['${outputs.map((output) => output.name).join(`', '`)}']`);
103
- }
104
99
  const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
105
100
  const outputsInterface = new Set();
106
101
  const outputReferenceRemap = {};
@@ -112,23 +107,23 @@ const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir)
112
107
  const remappedReference = `I${cmpMeta.componentClassName}${reference}`;
113
108
  if (refObject.location === 'local' || refObject.location === 'import') {
114
109
  outputReferenceRemap[reference] = remappedReference;
115
- outputsInterface.add(`import { ${reference} as ${remappedReference} } from '${componentCorePackage}';`);
110
+ let importLocation = componentCorePackage;
111
+ if (componentCorePackage !== undefined) {
112
+ const dirPath = includeImportCustomElements ? `/${customElementsDir || 'components'}` : '';
113
+ importLocation = `${normalizePath(componentCorePackage)}${dirPath}`;
114
+ }
115
+ outputsInterface.add(`import type { ${reference} as ${remappedReference} } from '${importLocation}';`);
116
116
  }
117
117
  });
118
118
  });
119
- const lines = [
120
- '',
121
- `${[...outputsInterface].join('\n')}
122
- export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {}
123
- ${getProxyCmp(inputs, methods)}
124
- @Component({
125
- ${directiveOpts.join(',\n ')}
126
- })
127
- export class ${tagNameAsPascal} {`,
119
+ const componentEvents = [
120
+ '' // Empty first line
128
121
  ];
129
122
  // Generate outputs
130
- outputs.forEach((output) => {
131
- lines.push(` /** ${output.docs.text} ${output.docs.tags.map((tag) => `@${tag.name} ${tag.text}`)}*/`);
123
+ outputs.forEach((output, index) => {
124
+ componentEvents.push(` /**
125
+ * ${output.docs.text} ${output.docs.tags.map((tag) => `@${tag.name} ${tag.text}`)}
126
+ */`);
132
127
  /**
133
128
  * The original attribute contains the original type defined by the devs.
134
129
  * This regexp normalizes the reference, by removing linebreaks,
@@ -142,8 +137,23 @@ export class ${tagNameAsPascal} {`,
142
137
  .replace(/\n/g, ' ')
143
138
  .replace(/\s{2,}/g, ' ')
144
139
  .replace(/,\s*/g, ', '));
145
- lines.push(` ${output.name}!: EventEmitter<CustomEvent<${outputTypeRemapped.trim()}>>;`);
140
+ componentEvents.push(` ${output.name}: EventEmitter<CustomEvent<${outputTypeRemapped.trim()}>>;`);
141
+ if (index === outputs.length - 1) {
142
+ // Empty line to push end `}` to new line
143
+ componentEvents.push('\n');
144
+ }
146
145
  });
146
+ const lines = [
147
+ '',
148
+ `${[...outputsInterface].join('\n')}
149
+ export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {${componentEvents.length > 1 ? componentEvents.join('\n') : ''}}
150
+
151
+ ${getProxyCmp(cmpMeta.tagName, includeImportCustomElements, inputs, methods)}
152
+ @Component({
153
+ ${directiveOpts.join(',\n ')}
154
+ })
155
+ export class ${tagNameAsPascal} {`,
156
+ ];
147
157
  lines.push(' protected el: HTMLElement;');
148
158
  lines.push(` constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
149
159
  c.detach();
@@ -155,13 +165,13 @@ export class ${tagNameAsPascal} {`,
155
165
  lines.push(`}`);
156
166
  return lines.join('\n');
157
167
  };
158
- function getProxyCmp(inputs, methods) {
168
+ function getProxyCmp(tagName, includeCustomElement, inputs, methods) {
159
169
  const hasInputs = inputs.length > 0;
160
170
  const hasMethods = methods.length > 0;
161
- const proxMeta = [];
162
- if (!hasInputs && !hasMethods) {
163
- return '';
164
- }
171
+ const proxMeta = [
172
+ `tagName: \'${tagName}\'`,
173
+ `customElement: ${includeCustomElement ? 'define' + dashToPascalCase(tagName) : 'undefined'}`
174
+ ];
165
175
  if (hasInputs)
166
176
  proxMeta.push(`inputs: ['${inputs.join(`', '`)}']`);
167
177
  if (hasMethods)
@@ -250,7 +260,7 @@ const VALUE_ACCESSOR_EVENTTARGETS = ` '(<VALUE_ACCESSOR_EVENT>)': 'handleChan
250
260
  async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
251
261
  const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
252
262
  const rootDir = config.rootDir;
253
- const pkgData = await readPackageJson(rootDir);
263
+ const pkgData = await readPackageJson(config, rootDir);
254
264
  const finalText = generateProxies(filteredComponents, pkgData, outputTarget, config.rootDir);
255
265
  await Promise.all([
256
266
  compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText),
@@ -285,14 +295,39 @@ function generateProxies(components, pkgData, outputTarget, rootDir) {
285
295
  /* auto-generated angular directive proxies */
286
296
  import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, NgZone } from '@angular/core';
287
297
  import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';\n`;
288
- const typeImports = !outputTarget.componentCorePackage
289
- ? `import { ${IMPORT_TYPES} } from '${normalizePath(componentsTypeFile)}';`
290
- : `import { ${IMPORT_TYPES} } from '${normalizePath(outputTarget.componentCorePackage)}';`;
298
+ /**
299
+ * Generate JSX import type from correct location.
300
+ * When using custom elements build, we need to import from
301
+ * either the "components" directory or customElementsDir
302
+ * otherwise we risk bundlers pulling in lazy loaded imports.
303
+ */
304
+ const generateTypeImports = () => {
305
+ let importLocation = outputTarget.componentCorePackage ? normalizePath(outputTarget.componentCorePackage) : normalizePath(componentsTypeFile);
306
+ importLocation += outputTarget.includeImportCustomElements ? `/${outputTarget.customElementsDir || 'components'}` : '';
307
+ return `import ${outputTarget.includeImportCustomElements ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
308
+ };
309
+ const typeImports = generateTypeImports();
310
+ let sourceImports = '';
311
+ /**
312
+ * Build an array of Custom Elements build imports and namespace them
313
+ * so that they do not conflict with the React wrapper names. For example,
314
+ * IonButton would be imported as IonButtonCmp so as to not conflict with the
315
+ * IonButton React Component that takes in the Web Component as a parameter.
316
+ */
317
+ if (outputTarget.includeImportCustomElements && outputTarget.componentCorePackage !== undefined) {
318
+ const cmpImports = components.map(component => {
319
+ const pascalImport = dashToPascalCase(component.tagName);
320
+ return `import { defineCustomElement as define${pascalImport} } from '${normalizePath(outputTarget.componentCorePackage)}/${outputTarget.customElementsDir ||
321
+ 'components'}/${component.tagName}.js';`;
322
+ });
323
+ sourceImports = cmpImports.join('\n');
324
+ }
291
325
  const final = [
292
326
  imports,
293
327
  typeImports,
328
+ sourceImports,
294
329
  components
295
- .map(createComponentDefinition(outputTarget.componentCorePackage))
330
+ .map(createComponentDefinition(outputTarget.componentCorePackage, distTypesDir, rootDir, outputTarget.includeImportCustomElements, outputTarget.customElementsDir))
296
331
  .join('\n'),
297
332
  ];
298
333
  return final.join('\n') + '\n';
@@ -1,12 +1,12 @@
1
1
  import path from 'path';
2
- import { relativeImport, normalizePath, sortBy, readPackageJson } from './utils';
2
+ import { relativeImport, normalizePath, sortBy, readPackageJson, dashToPascalCase } from './utils';
3
3
  import { createComponentDefinition } from './generate-angular-component';
4
4
  import { generateAngularDirectivesFile } from './generate-angular-directives-file';
5
5
  import generateValueAccessors from './generate-value-accessors';
6
6
  export async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
7
7
  const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
8
8
  const rootDir = config.rootDir;
9
- const pkgData = await readPackageJson(rootDir);
9
+ const pkgData = await readPackageJson(config, rootDir);
10
10
  const finalText = generateProxies(filteredComponents, pkgData, outputTarget, config.rootDir);
11
11
  await Promise.all([
12
12
  compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText),
@@ -41,14 +41,39 @@ export function generateProxies(components, pkgData, outputTarget, rootDir) {
41
41
  /* auto-generated angular directive proxies */
42
42
  import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, NgZone } from '@angular/core';
43
43
  import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';\n`;
44
- const typeImports = !outputTarget.componentCorePackage
45
- ? `import { ${IMPORT_TYPES} } from '${normalizePath(componentsTypeFile)}';`
46
- : `import { ${IMPORT_TYPES} } from '${normalizePath(outputTarget.componentCorePackage)}';`;
44
+ /**
45
+ * Generate JSX import type from correct location.
46
+ * When using custom elements build, we need to import from
47
+ * either the "components" directory or customElementsDir
48
+ * otherwise we risk bundlers pulling in lazy loaded imports.
49
+ */
50
+ const generateTypeImports = () => {
51
+ let importLocation = outputTarget.componentCorePackage ? normalizePath(outputTarget.componentCorePackage) : normalizePath(componentsTypeFile);
52
+ importLocation += outputTarget.includeImportCustomElements ? `/${outputTarget.customElementsDir || 'components'}` : '';
53
+ return `import ${outputTarget.includeImportCustomElements ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
54
+ };
55
+ const typeImports = generateTypeImports();
56
+ let sourceImports = '';
57
+ /**
58
+ * Build an array of Custom Elements build imports and namespace them
59
+ * so that they do not conflict with the React wrapper names. For example,
60
+ * IonButton would be imported as IonButtonCmp so as to not conflict with the
61
+ * IonButton React Component that takes in the Web Component as a parameter.
62
+ */
63
+ if (outputTarget.includeImportCustomElements && outputTarget.componentCorePackage !== undefined) {
64
+ const cmpImports = components.map(component => {
65
+ const pascalImport = dashToPascalCase(component.tagName);
66
+ return `import { defineCustomElement as define${pascalImport} } from '${normalizePath(outputTarget.componentCorePackage)}/${outputTarget.customElementsDir ||
67
+ 'components'}/${component.tagName}.js';`;
68
+ });
69
+ sourceImports = cmpImports.join('\n');
70
+ }
47
71
  const final = [
48
72
  imports,
49
73
  typeImports,
74
+ sourceImports,
50
75
  components
51
- .map(createComponentDefinition(outputTarget.componentCorePackage, distTypesDir, rootDir))
76
+ .map(createComponentDefinition(outputTarget.componentCorePackage, distTypesDir, rootDir, outputTarget.includeImportCustomElements, outputTarget.customElementsDir))
52
77
  .join('\n'),
53
78
  ];
54
79
  return final.join('\n') + '\n';
package/dist/types.d.ts CHANGED
@@ -5,6 +5,8 @@ export interface OutputTargetAngular {
5
5
  directivesUtilsFile?: string;
6
6
  valueAccessorConfigs?: ValueAccessorConfig[];
7
7
  excludeComponents?: string[];
8
+ includeImportCustomElements?: boolean;
9
+ customElementsDir?: string;
8
10
  }
9
11
  export declare type ValueAccessorTypes = 'text' | 'radio' | 'select' | 'number' | 'boolean';
10
12
  export interface ValueAccessorConfig {
package/dist/utils.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Config } from '@stencil/core/internal';
1
2
  import type { PackageJSON } from './types';
2
3
  export declare const toLowerCase: (str: string) => string;
3
4
  export declare const dashToPascalCase: (str: string) => string;
@@ -5,4 +6,4 @@ export declare function sortBy<T>(array: T[], prop: (item: T) => string): T[];
5
6
  export declare function normalizePath(str: string): string;
6
7
  export declare function relativeImport(pathFrom: string, pathTo: string, ext?: string): string;
7
8
  export declare function isRelativePath(path: string): boolean | "";
8
- export declare function readPackageJson(rootDir: string): Promise<PackageJSON>;
9
+ export declare function readPackageJson(config: Config, rootDir: string): Promise<PackageJSON>;
package/dist/utils.js CHANGED
@@ -1,7 +1,4 @@
1
1
  import path from 'path';
2
- import { promisify } from 'util';
3
- import fs from 'fs';
4
- const readFile = promisify(fs.readFile);
5
2
  export const toLowerCase = (str) => str.toLowerCase();
6
3
  export const dashToPascalCase = (str) => toLowerCase(str)
7
4
  .split('-')
@@ -58,11 +55,12 @@ export function relativeImport(pathFrom, pathTo, ext) {
58
55
  export function isRelativePath(path) {
59
56
  return path && path.startsWith('.');
60
57
  }
61
- export async function readPackageJson(rootDir) {
58
+ export async function readPackageJson(config, rootDir) {
59
+ var _a;
62
60
  const pkgJsonPath = path.join(rootDir, 'package.json');
63
61
  let pkgJson;
64
62
  try {
65
- pkgJson = await readFile(pkgJsonPath, 'utf8');
63
+ pkgJson = (await ((_a = config.sys) === null || _a === void 0 ? void 0 : _a.readFile(pkgJsonPath, 'utf8')));
66
64
  }
67
65
  catch (e) {
68
66
  throw new Error(`Missing "package.json" file for distribution: ${pkgJsonPath}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stencil/angular-output-target",
3
- "version": "0.2.0",
3
+ "version": "0.4.0-0",
4
4
  "description": "Angular output target for @stencil/core components.",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.js",