@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.
- package/angular-component-lib/utils.ts +23 -6
- package/dist/generate-angular-component.d.ts +1 -1
- package/dist/generate-angular-component.js +35 -23
- package/dist/index.cjs.js +67 -33
- package/dist/index.js +67 -32
- package/dist/output-angular.js +31 -6
- package/dist/types.d.ts +2 -0
- package/dist/utils.d.ts +2 -1
- package/dist/utils.js +3 -5
- package/package.json +1 -1
|
@@ -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
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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 (
|
|
41
|
-
proxyMethods(cls,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
172
|
-
|
|
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
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
163
|
-
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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';
|
package/dist/output-angular.js
CHANGED
|
@@ -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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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}`);
|