@stencil/angular-output-target 0.1.0 → 0.3.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 +21 -6
- package/dist/generate-angular-component.d.ts +1 -1
- package/dist/generate-angular-component.js +39 -35
- package/dist/index.cjs.js +71 -47
- package/dist/index.js +71 -46
- 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,28 @@ 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, customElement?: any, inputs?: any; methods?: any }) {
|
|
47
|
+
const decorator = function (cls: any) {
|
|
48
|
+
const { tagName, customElement, inputs, methods } = opts;
|
|
49
|
+
|
|
50
|
+
defineCustomElement(tagName, customElement);
|
|
51
|
+
|
|
52
|
+
if (inputs) {
|
|
53
|
+
proxyInputs(cls, inputs);
|
|
39
54
|
}
|
|
40
|
-
if (
|
|
41
|
-
proxyMethods(cls,
|
|
55
|
+
if (methods) {
|
|
56
|
+
proxyMethods(cls, methods);
|
|
42
57
|
}
|
|
43
58
|
return cls;
|
|
44
59
|
};
|
|
@@ -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,6 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
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) => {
|
|
4
3
|
// Collect component meta
|
|
5
4
|
const inputs = [
|
|
6
5
|
...cmpMeta.properties.filter((prop) => !prop.internal).map((prop) => prop.name),
|
|
@@ -19,44 +18,34 @@ export const createComponentDefinition = (componentCorePackage, distTypesDir, ro
|
|
|
19
18
|
if (inputs.length > 0) {
|
|
20
19
|
directiveOpts.push(`inputs: ['${inputs.join(`', '`)}']`);
|
|
21
20
|
}
|
|
22
|
-
if (outputs.length > 0) {
|
|
23
|
-
directiveOpts.push(`outputs: ['${outputs.map((output) => output.name).join(`', '`)}']`);
|
|
24
|
-
}
|
|
25
21
|
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
26
|
-
const typePath = path.parse(path.join(componentCorePackage, path.join(cmpMeta.sourceFilePath, '').replace(path.join(rootDir, 'src'), distTypesDir)));
|
|
27
|
-
const importPath = normalizePath(path.join(typePath.dir, typePath.name));
|
|
28
22
|
const outputsInterface = new Set();
|
|
29
23
|
const outputReferenceRemap = {};
|
|
30
24
|
outputs.forEach((output) => {
|
|
31
25
|
Object.entries(output.complexType.references).forEach(([reference, refObject]) => {
|
|
32
|
-
// Add import line for each local/import reference, and add new mapping name
|
|
33
|
-
// outputReferenceRemap should be updated only if the import interface is set in outputsInterface
|
|
34
|
-
//
|
|
26
|
+
// Add import line for each local/import reference, and add new mapping name.
|
|
27
|
+
// `outputReferenceRemap` should be updated only if the import interface is set in outputsInterface,
|
|
28
|
+
// this will prevent global types to be remapped.
|
|
35
29
|
const remappedReference = `I${cmpMeta.componentClassName}${reference}`;
|
|
36
|
-
if (refObject.location === 'local') {
|
|
37
|
-
outputReferenceRemap[reference] = remappedReference;
|
|
38
|
-
outputsInterface.add(`import { ${reference} as ${remappedReference} } from '${importPath}';`);
|
|
39
|
-
}
|
|
40
|
-
else if (refObject.location === 'import') {
|
|
30
|
+
if (refObject.location === 'local' || refObject.location === 'import') {
|
|
41
31
|
outputReferenceRemap[reference] = remappedReference;
|
|
42
|
-
|
|
43
|
-
|
|
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}';`);
|
|
44
38
|
}
|
|
45
39
|
});
|
|
46
40
|
});
|
|
47
|
-
const
|
|
48
|
-
''
|
|
49
|
-
`${[...outputsInterface].join('\n')}
|
|
50
|
-
export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {}
|
|
51
|
-
${getProxyCmp(inputs, methods)}
|
|
52
|
-
@Component({
|
|
53
|
-
${directiveOpts.join(',\n ')}
|
|
54
|
-
})
|
|
55
|
-
export class ${tagNameAsPascal} {`,
|
|
41
|
+
const componentEvents = [
|
|
42
|
+
'' // Empty first line
|
|
56
43
|
];
|
|
57
44
|
// Generate outputs
|
|
58
|
-
outputs.forEach((output) => {
|
|
59
|
-
|
|
45
|
+
outputs.forEach((output, index) => {
|
|
46
|
+
componentEvents.push(` /**
|
|
47
|
+
* ${output.docs.text} ${output.docs.tags.map((tag) => `@${tag.name} ${tag.text}`)}
|
|
48
|
+
*/`);
|
|
60
49
|
/**
|
|
61
50
|
* The original attribute contains the original type defined by the devs.
|
|
62
51
|
* This regexp normalizes the reference, by removing linebreaks,
|
|
@@ -70,8 +59,23 @@ export class ${tagNameAsPascal} {`,
|
|
|
70
59
|
.replace(/\n/g, ' ')
|
|
71
60
|
.replace(/\s{2,}/g, ' ')
|
|
72
61
|
.replace(/,\s*/g, ', '));
|
|
73
|
-
|
|
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
|
+
}
|
|
74
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
|
+
];
|
|
75
79
|
lines.push(' protected el: HTMLElement;');
|
|
76
80
|
lines.push(` constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
|
77
81
|
c.detach();
|
|
@@ -83,13 +87,13 @@ export class ${tagNameAsPascal} {`,
|
|
|
83
87
|
lines.push(`}`);
|
|
84
88
|
return lines.join('\n');
|
|
85
89
|
};
|
|
86
|
-
function getProxyCmp(inputs, methods) {
|
|
90
|
+
function getProxyCmp(tagName, includeCustomElement, inputs, methods) {
|
|
87
91
|
const hasInputs = inputs.length > 0;
|
|
88
92
|
const hasMethods = methods.length > 0;
|
|
89
|
-
const proxMeta = [
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
const proxMeta = [
|
|
94
|
+
`tagName: \'${tagName}\'`,
|
|
95
|
+
`customElement: ${includeCustomElement ? dashToPascalCase(tagName) + 'Cmp' : 'undefined'}`
|
|
96
|
+
];
|
|
93
97
|
if (hasInputs)
|
|
94
98
|
proxMeta.push(`inputs: ['${inputs.join(`', '`)}']`);
|
|
95
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,14 +62,12 @@ function relativeImport(pathFrom, pathTo, ext) {
|
|
|
66
62
|
}
|
|
67
63
|
return normalizePath(`${relativePath}/${path__default['default'].basename(pathTo, ext)}`);
|
|
68
64
|
}
|
|
69
|
-
function
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
async function readPackageJson(rootDir) {
|
|
65
|
+
async function readPackageJson(config, rootDir) {
|
|
66
|
+
var _a;
|
|
73
67
|
const pkgJsonPath = path__default['default'].join(rootDir, 'package.json');
|
|
74
68
|
let pkgJson;
|
|
75
69
|
try {
|
|
76
|
-
pkgJson = await readFile(pkgJsonPath, 'utf8');
|
|
70
|
+
pkgJson = (await ((_a = config.sys) === null || _a === void 0 ? void 0 : _a.readFile(pkgJsonPath, 'utf8')));
|
|
77
71
|
}
|
|
78
72
|
catch (e) {
|
|
79
73
|
throw new Error(`Missing "package.json" file for distribution: ${pkgJsonPath}`);
|
|
@@ -91,7 +85,7 @@ const EXTENDED_PATH_REGEX = /^\\\\\?\\/;
|
|
|
91
85
|
const NON_ASCII_REGEX = /[^\x00-\x80]+/;
|
|
92
86
|
const SLASH_REGEX = /\\/g;
|
|
93
87
|
|
|
94
|
-
const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir) => (cmpMeta) => {
|
|
88
|
+
const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir, includeImportCustomElements = false, customElementsDir = 'components') => (cmpMeta) => {
|
|
95
89
|
// Collect component meta
|
|
96
90
|
const inputs = [
|
|
97
91
|
...cmpMeta.properties.filter((prop) => !prop.internal).map((prop) => prop.name),
|
|
@@ -110,44 +104,34 @@ const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir)
|
|
|
110
104
|
if (inputs.length > 0) {
|
|
111
105
|
directiveOpts.push(`inputs: ['${inputs.join(`', '`)}']`);
|
|
112
106
|
}
|
|
113
|
-
if (outputs.length > 0) {
|
|
114
|
-
directiveOpts.push(`outputs: ['${outputs.map((output) => output.name).join(`', '`)}']`);
|
|
115
|
-
}
|
|
116
107
|
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
117
|
-
const typePath = path__default['default'].parse(path__default['default'].join(componentCorePackage, path__default['default'].join(cmpMeta.sourceFilePath, '').replace(path__default['default'].join(rootDir, 'src'), distTypesDir)));
|
|
118
|
-
const importPath = normalizePath(path__default['default'].join(typePath.dir, typePath.name));
|
|
119
108
|
const outputsInterface = new Set();
|
|
120
109
|
const outputReferenceRemap = {};
|
|
121
110
|
outputs.forEach((output) => {
|
|
122
111
|
Object.entries(output.complexType.references).forEach(([reference, refObject]) => {
|
|
123
|
-
// Add import line for each local/import reference, and add new mapping name
|
|
124
|
-
// outputReferenceRemap should be updated only if the import interface is set in outputsInterface
|
|
125
|
-
//
|
|
112
|
+
// Add import line for each local/import reference, and add new mapping name.
|
|
113
|
+
// `outputReferenceRemap` should be updated only if the import interface is set in outputsInterface,
|
|
114
|
+
// this will prevent global types to be remapped.
|
|
126
115
|
const remappedReference = `I${cmpMeta.componentClassName}${reference}`;
|
|
127
|
-
if (refObject.location === 'local') {
|
|
128
|
-
outputReferenceRemap[reference] = remappedReference;
|
|
129
|
-
outputsInterface.add(`import { ${reference} as ${remappedReference} } from '${importPath}';`);
|
|
130
|
-
}
|
|
131
|
-
else if (refObject.location === 'import') {
|
|
116
|
+
if (refObject.location === 'local' || refObject.location === 'import') {
|
|
132
117
|
outputReferenceRemap[reference] = remappedReference;
|
|
133
|
-
|
|
134
|
-
|
|
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}';`);
|
|
135
124
|
}
|
|
136
125
|
});
|
|
137
126
|
});
|
|
138
|
-
const
|
|
139
|
-
''
|
|
140
|
-
`${[...outputsInterface].join('\n')}
|
|
141
|
-
export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {}
|
|
142
|
-
${getProxyCmp(inputs, methods)}
|
|
143
|
-
@Component({
|
|
144
|
-
${directiveOpts.join(',\n ')}
|
|
145
|
-
})
|
|
146
|
-
export class ${tagNameAsPascal} {`,
|
|
127
|
+
const componentEvents = [
|
|
128
|
+
'' // Empty first line
|
|
147
129
|
];
|
|
148
130
|
// Generate outputs
|
|
149
|
-
outputs.forEach((output) => {
|
|
150
|
-
|
|
131
|
+
outputs.forEach((output, index) => {
|
|
132
|
+
componentEvents.push(` /**
|
|
133
|
+
* ${output.docs.text} ${output.docs.tags.map((tag) => `@${tag.name} ${tag.text}`)}
|
|
134
|
+
*/`);
|
|
151
135
|
/**
|
|
152
136
|
* The original attribute contains the original type defined by the devs.
|
|
153
137
|
* This regexp normalizes the reference, by removing linebreaks,
|
|
@@ -161,8 +145,23 @@ export class ${tagNameAsPascal} {`,
|
|
|
161
145
|
.replace(/\n/g, ' ')
|
|
162
146
|
.replace(/\s{2,}/g, ' ')
|
|
163
147
|
.replace(/,\s*/g, ', '));
|
|
164
|
-
|
|
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
|
+
}
|
|
165
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
|
+
];
|
|
166
165
|
lines.push(' protected el: HTMLElement;');
|
|
167
166
|
lines.push(` constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
|
168
167
|
c.detach();
|
|
@@ -174,13 +173,13 @@ export class ${tagNameAsPascal} {`,
|
|
|
174
173
|
lines.push(`}`);
|
|
175
174
|
return lines.join('\n');
|
|
176
175
|
};
|
|
177
|
-
function getProxyCmp(inputs, methods) {
|
|
176
|
+
function getProxyCmp(tagName, includeCustomElement, inputs, methods) {
|
|
178
177
|
const hasInputs = inputs.length > 0;
|
|
179
178
|
const hasMethods = methods.length > 0;
|
|
180
|
-
const proxMeta = [
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
179
|
+
const proxMeta = [
|
|
180
|
+
`tagName: \'${tagName}\'`,
|
|
181
|
+
`customElement: ${includeCustomElement ? dashToPascalCase(tagName) + 'Cmp' : 'undefined'}`
|
|
182
|
+
];
|
|
184
183
|
if (hasInputs)
|
|
185
184
|
proxMeta.push(`inputs: ['${inputs.join(`', '`)}']`);
|
|
186
185
|
if (hasMethods)
|
|
@@ -269,7 +268,7 @@ const VALUE_ACCESSOR_EVENTTARGETS = ` '(<VALUE_ACCESSOR_EVENT>)': 'handleChan
|
|
|
269
268
|
async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
|
|
270
269
|
const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
|
|
271
270
|
const rootDir = config.rootDir;
|
|
272
|
-
const pkgData = await readPackageJson(rootDir);
|
|
271
|
+
const pkgData = await readPackageJson(config, rootDir);
|
|
273
272
|
const finalText = generateProxies(filteredComponents, pkgData, outputTarget, config.rootDir);
|
|
274
273
|
await Promise.all([
|
|
275
274
|
compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText),
|
|
@@ -304,14 +303,39 @@ function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
|
304
303
|
/* auto-generated angular directive proxies */
|
|
305
304
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, NgZone } from '@angular/core';
|
|
306
305
|
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';\n`;
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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 { ${pascalImport} as ${pascalImport}Cmp } from '${normalizePath(outputTarget.componentCorePackage)}/${outputTarget.customElementsDir ||
|
|
329
|
+
'components'}/${component.tagName}.js';`;
|
|
330
|
+
});
|
|
331
|
+
sourceImports = cmpImports.join('\n');
|
|
332
|
+
}
|
|
310
333
|
const final = [
|
|
311
334
|
imports,
|
|
312
335
|
typeImports,
|
|
336
|
+
sourceImports,
|
|
313
337
|
components
|
|
314
|
-
.map(createComponentDefinition(outputTarget.componentCorePackage, distTypesDir, rootDir))
|
|
338
|
+
.map(createComponentDefinition(outputTarget.componentCorePackage, distTypesDir, rootDir, outputTarget.includeImportCustomElements, outputTarget.customElementsDir))
|
|
315
339
|
.join('\n'),
|
|
316
340
|
];
|
|
317
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,14 +54,12 @@ function relativeImport(pathFrom, pathTo, ext) {
|
|
|
57
54
|
}
|
|
58
55
|
return normalizePath(`${relativePath}/${path.basename(pathTo, ext)}`);
|
|
59
56
|
}
|
|
60
|
-
function
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
async function readPackageJson(rootDir) {
|
|
57
|
+
async function readPackageJson(config, rootDir) {
|
|
58
|
+
var _a;
|
|
64
59
|
const pkgJsonPath = path.join(rootDir, 'package.json');
|
|
65
60
|
let pkgJson;
|
|
66
61
|
try {
|
|
67
|
-
pkgJson = await readFile(pkgJsonPath, 'utf8');
|
|
62
|
+
pkgJson = (await ((_a = config.sys) === null || _a === void 0 ? void 0 : _a.readFile(pkgJsonPath, 'utf8')));
|
|
68
63
|
}
|
|
69
64
|
catch (e) {
|
|
70
65
|
throw new Error(`Missing "package.json" file for distribution: ${pkgJsonPath}`);
|
|
@@ -82,7 +77,7 @@ const EXTENDED_PATH_REGEX = /^\\\\\?\\/;
|
|
|
82
77
|
const NON_ASCII_REGEX = /[^\x00-\x80]+/;
|
|
83
78
|
const SLASH_REGEX = /\\/g;
|
|
84
79
|
|
|
85
|
-
const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir) => (cmpMeta) => {
|
|
80
|
+
const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir, includeImportCustomElements = false, customElementsDir = 'components') => (cmpMeta) => {
|
|
86
81
|
// Collect component meta
|
|
87
82
|
const inputs = [
|
|
88
83
|
...cmpMeta.properties.filter((prop) => !prop.internal).map((prop) => prop.name),
|
|
@@ -101,44 +96,34 @@ const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir)
|
|
|
101
96
|
if (inputs.length > 0) {
|
|
102
97
|
directiveOpts.push(`inputs: ['${inputs.join(`', '`)}']`);
|
|
103
98
|
}
|
|
104
|
-
if (outputs.length > 0) {
|
|
105
|
-
directiveOpts.push(`outputs: ['${outputs.map((output) => output.name).join(`', '`)}']`);
|
|
106
|
-
}
|
|
107
99
|
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
108
|
-
const typePath = path.parse(path.join(componentCorePackage, path.join(cmpMeta.sourceFilePath, '').replace(path.join(rootDir, 'src'), distTypesDir)));
|
|
109
|
-
const importPath = normalizePath(path.join(typePath.dir, typePath.name));
|
|
110
100
|
const outputsInterface = new Set();
|
|
111
101
|
const outputReferenceRemap = {};
|
|
112
102
|
outputs.forEach((output) => {
|
|
113
103
|
Object.entries(output.complexType.references).forEach(([reference, refObject]) => {
|
|
114
|
-
// Add import line for each local/import reference, and add new mapping name
|
|
115
|
-
// outputReferenceRemap should be updated only if the import interface is set in outputsInterface
|
|
116
|
-
//
|
|
104
|
+
// Add import line for each local/import reference, and add new mapping name.
|
|
105
|
+
// `outputReferenceRemap` should be updated only if the import interface is set in outputsInterface,
|
|
106
|
+
// this will prevent global types to be remapped.
|
|
117
107
|
const remappedReference = `I${cmpMeta.componentClassName}${reference}`;
|
|
118
|
-
if (refObject.location === 'local') {
|
|
119
|
-
outputReferenceRemap[reference] = remappedReference;
|
|
120
|
-
outputsInterface.add(`import { ${reference} as ${remappedReference} } from '${importPath}';`);
|
|
121
|
-
}
|
|
122
|
-
else if (refObject.location === 'import') {
|
|
108
|
+
if (refObject.location === 'local' || refObject.location === 'import') {
|
|
123
109
|
outputReferenceRemap[reference] = remappedReference;
|
|
124
|
-
|
|
125
|
-
|
|
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}';`);
|
|
126
116
|
}
|
|
127
117
|
});
|
|
128
118
|
});
|
|
129
|
-
const
|
|
130
|
-
''
|
|
131
|
-
`${[...outputsInterface].join('\n')}
|
|
132
|
-
export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {}
|
|
133
|
-
${getProxyCmp(inputs, methods)}
|
|
134
|
-
@Component({
|
|
135
|
-
${directiveOpts.join(',\n ')}
|
|
136
|
-
})
|
|
137
|
-
export class ${tagNameAsPascal} {`,
|
|
119
|
+
const componentEvents = [
|
|
120
|
+
'' // Empty first line
|
|
138
121
|
];
|
|
139
122
|
// Generate outputs
|
|
140
|
-
outputs.forEach((output) => {
|
|
141
|
-
|
|
123
|
+
outputs.forEach((output, index) => {
|
|
124
|
+
componentEvents.push(` /**
|
|
125
|
+
* ${output.docs.text} ${output.docs.tags.map((tag) => `@${tag.name} ${tag.text}`)}
|
|
126
|
+
*/`);
|
|
142
127
|
/**
|
|
143
128
|
* The original attribute contains the original type defined by the devs.
|
|
144
129
|
* This regexp normalizes the reference, by removing linebreaks,
|
|
@@ -152,8 +137,23 @@ export class ${tagNameAsPascal} {`,
|
|
|
152
137
|
.replace(/\n/g, ' ')
|
|
153
138
|
.replace(/\s{2,}/g, ' ')
|
|
154
139
|
.replace(/,\s*/g, ', '));
|
|
155
|
-
|
|
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
|
+
}
|
|
156
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
|
+
];
|
|
157
157
|
lines.push(' protected el: HTMLElement;');
|
|
158
158
|
lines.push(` constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
|
159
159
|
c.detach();
|
|
@@ -165,13 +165,13 @@ export class ${tagNameAsPascal} {`,
|
|
|
165
165
|
lines.push(`}`);
|
|
166
166
|
return lines.join('\n');
|
|
167
167
|
};
|
|
168
|
-
function getProxyCmp(inputs, methods) {
|
|
168
|
+
function getProxyCmp(tagName, includeCustomElement, inputs, methods) {
|
|
169
169
|
const hasInputs = inputs.length > 0;
|
|
170
170
|
const hasMethods = methods.length > 0;
|
|
171
|
-
const proxMeta = [
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
171
|
+
const proxMeta = [
|
|
172
|
+
`tagName: \'${tagName}\'`,
|
|
173
|
+
`customElement: ${includeCustomElement ? dashToPascalCase(tagName) + 'Cmp' : 'undefined'}`
|
|
174
|
+
];
|
|
175
175
|
if (hasInputs)
|
|
176
176
|
proxMeta.push(`inputs: ['${inputs.join(`', '`)}']`);
|
|
177
177
|
if (hasMethods)
|
|
@@ -260,7 +260,7 @@ const VALUE_ACCESSOR_EVENTTARGETS = ` '(<VALUE_ACCESSOR_EVENT>)': 'handleChan
|
|
|
260
260
|
async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
|
|
261
261
|
const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
|
|
262
262
|
const rootDir = config.rootDir;
|
|
263
|
-
const pkgData = await readPackageJson(rootDir);
|
|
263
|
+
const pkgData = await readPackageJson(config, rootDir);
|
|
264
264
|
const finalText = generateProxies(filteredComponents, pkgData, outputTarget, config.rootDir);
|
|
265
265
|
await Promise.all([
|
|
266
266
|
compilerCtx.fs.writeFile(outputTarget.directivesProxyFile, finalText),
|
|
@@ -295,14 +295,39 @@ function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
|
295
295
|
/* auto-generated angular directive proxies */
|
|
296
296
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, NgZone } from '@angular/core';
|
|
297
297
|
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';\n`;
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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 { ${pascalImport} as ${pascalImport}Cmp } from '${normalizePath(outputTarget.componentCorePackage)}/${outputTarget.customElementsDir ||
|
|
321
|
+
'components'}/${component.tagName}.js';`;
|
|
322
|
+
});
|
|
323
|
+
sourceImports = cmpImports.join('\n');
|
|
324
|
+
}
|
|
301
325
|
const final = [
|
|
302
326
|
imports,
|
|
303
327
|
typeImports,
|
|
328
|
+
sourceImports,
|
|
304
329
|
components
|
|
305
|
-
.map(createComponentDefinition(outputTarget.componentCorePackage, distTypesDir, rootDir))
|
|
330
|
+
.map(createComponentDefinition(outputTarget.componentCorePackage, distTypesDir, rootDir, outputTarget.includeImportCustomElements, outputTarget.customElementsDir))
|
|
306
331
|
.join('\n'),
|
|
307
332
|
];
|
|
308
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 { ${pascalImport} as ${pascalImport}Cmp } 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}`);
|