@stencil/angular-output-target 0.6.1-dev.11655483906.1e51ded4 → 0.6.1-dev.11662151255.17ea4066
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/README.md +2 -2
- package/angular-component-lib/utils.ts +9 -15
- package/dist/generate-angular-component.d.ts +22 -2
- package/dist/generate-angular-component.js +117 -100
- package/dist/generate-angular-directives-file.js +1 -1
- package/dist/generate-angular-modules.d.ts +6 -0
- package/dist/generate-angular-modules.js +17 -0
- package/dist/generate-value-accessors.js +3 -6
- package/dist/index.cjs.js +261 -155
- package/dist/index.js +261 -155
- package/dist/output-angular.js +78 -40
- package/dist/plugin.d.ts +1 -1
- package/dist/plugin.js +8 -5
- package/dist/types.d.ts +18 -2
- package/dist/utils.d.ts +24 -6
- package/dist/utils.js +42 -7
- package/package.json +6 -3
package/dist/index.cjs.js
CHANGED
|
@@ -82,132 +82,184 @@ async function readPackageJson(config, rootDir) {
|
|
|
82
82
|
return pkgData;
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
85
|
+
* Formats an array of strings to a string of quoted, comma separated values.
|
|
86
|
+
* @param list The list of unformatted strings to format
|
|
87
|
+
* @returns The formatted array of strings. (e.g. ['foo', 'bar']) => `'foo', 'bar'`
|
|
88
|
+
*/
|
|
89
|
+
const formatToQuotedList = (list) => list.map((item) => `'${item}'`).join(', ');
|
|
90
|
+
/**
|
|
91
|
+
* Creates an import statement for a list of named imports from a module.
|
|
92
|
+
* @param imports The list of named imports.
|
|
93
|
+
* @param module The module to import from.
|
|
87
94
|
*
|
|
88
|
-
* @
|
|
89
|
-
* @returns The formatted custom event interface name.
|
|
95
|
+
* @returns The import statement as a string.
|
|
90
96
|
*/
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const EXTENDED_PATH_REGEX = /^\\\\\?\\/;
|
|
95
|
-
const NON_ASCII_REGEX = /[^\x00-\x80]+/;
|
|
96
|
-
const SLASH_REGEX = /\\/g;
|
|
97
|
-
|
|
98
|
-
const createComponentDefinition = (componentCorePackage, distTypesDir, rootDir, includeImportCustomElements = false, customElementsDir = 'components') => (cmpMeta) => {
|
|
99
|
-
// Collect component meta
|
|
100
|
-
const inputs = [
|
|
101
|
-
...cmpMeta.properties.filter((prop) => !prop.internal).map((prop) => prop.name),
|
|
102
|
-
...cmpMeta.virtualProperties.map((prop) => prop.name),
|
|
103
|
-
].sort();
|
|
104
|
-
const outputs = cmpMeta.events.filter((ev) => !ev.internal).map((prop) => prop);
|
|
105
|
-
const methods = cmpMeta.methods.filter((method) => !method.internal).map((prop) => prop.name);
|
|
106
|
-
// Process meta
|
|
107
|
-
const hasOutputs = outputs.length > 0;
|
|
108
|
-
// Generate Angular @Directive
|
|
109
|
-
const directiveOpts = [
|
|
110
|
-
`selector: \'${cmpMeta.tagName}\'`,
|
|
111
|
-
`changeDetection: ChangeDetectionStrategy.OnPush`,
|
|
112
|
-
`template: '<ng-content></ng-content>'`,
|
|
113
|
-
];
|
|
114
|
-
if (inputs.length > 0) {
|
|
115
|
-
directiveOpts.push(`inputs: ['${inputs.join(`', '`)}']`);
|
|
97
|
+
const createImportStatement = (imports, module) => {
|
|
98
|
+
if (imports.length === 0) {
|
|
99
|
+
return '';
|
|
116
100
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
101
|
+
return `import { ${imports.join(', ')} } from '${module}';`;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Creates the collection of import statements for a component based on the component's events type dependencies.
|
|
105
|
+
* @param componentTagName The tag name of the component (pascal case).
|
|
106
|
+
* @param events The events compiler metadata.
|
|
107
|
+
* @param options The options for generating the import statements (e.g. whether to import from the custom elements directory).
|
|
108
|
+
* @returns The import statements as an array of strings.
|
|
109
|
+
*/
|
|
110
|
+
const createComponentEventTypeImports = (componentTagName, events, options) => {
|
|
111
|
+
const { componentCorePackage, includeImportCustomElements, customElementsDir } = options;
|
|
112
|
+
const imports = [];
|
|
113
|
+
const namedImports = new Set();
|
|
114
|
+
const importPathName = normalizePath(componentCorePackage) + (includeImportCustomElements ? `/${customElementsDir || 'components'}` : '');
|
|
115
|
+
events.forEach((event) => {
|
|
116
|
+
Object.entries(event.complexType.references).forEach(([typeName, refObject]) => {
|
|
126
117
|
if (refObject.location === 'local' || refObject.location === 'import') {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
118
|
+
const newTypeName = `I${componentTagName}${typeName}`;
|
|
119
|
+
// Prevents duplicate imports for the same type.
|
|
120
|
+
if (!namedImports.has(newTypeName)) {
|
|
121
|
+
imports.push(`import type { ${typeName} as ${newTypeName} } from '${importPathName}';`);
|
|
122
|
+
namedImports.add(newTypeName);
|
|
132
123
|
}
|
|
133
|
-
outputsInterface.add(`import type { ${reference} as ${remappedReference} } from '${importLocation}';`);
|
|
134
124
|
}
|
|
135
125
|
});
|
|
136
126
|
});
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
componentEvents.push(` /**
|
|
143
|
-
* ${output.docs.text} ${output.docs.tags.map((tag) => `@${tag.name} ${tag.text}`)}
|
|
144
|
-
*/`);
|
|
145
|
-
/**
|
|
146
|
-
* The original attribute contains the original type defined by the devs.
|
|
147
|
-
* This regexp normalizes the reference, by removing linebreaks,
|
|
148
|
-
* replacing consecutive spaces with a single space, and adding a single space after commas.
|
|
149
|
-
**/
|
|
150
|
-
const outputTypeRemapped = Object.entries(outputReferenceRemap).reduce((type, [src, dst]) => {
|
|
151
|
-
return type
|
|
152
|
-
.replace(new RegExp(`^${src}$`, 'g'), `${dst}`)
|
|
153
|
-
.replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (v, p1, p2) => [p1, dst, p2].join(''));
|
|
154
|
-
}, output.complexType.original
|
|
155
|
-
.replace(/\n/g, ' ')
|
|
156
|
-
.replace(/\s{2,}/g, ' ')
|
|
157
|
-
.replace(/,\s*/g, ', '));
|
|
158
|
-
/**
|
|
159
|
-
* Starting in Stencil 2.16.0, the compiler will generate a CustomEvent interface
|
|
160
|
-
* for each component with custom events.
|
|
161
|
-
*
|
|
162
|
-
* The name of this custom event is "ComponentTagNameCustomEvent".
|
|
163
|
-
*/
|
|
164
|
-
componentEvents.push(` ${output.name}: EventEmitter<${formatCustomEventInterfaceName(cmpMeta.tagName)}<${outputTypeRemapped.trim()}>>;`);
|
|
165
|
-
if (index === outputs.length - 1) {
|
|
166
|
-
// Empty line to push end `}` to new line
|
|
167
|
-
componentEvents.push('\n');
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
const lines = [
|
|
171
|
-
'',
|
|
172
|
-
`${[...outputsInterface].join('\n')}
|
|
173
|
-
export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {${componentEvents.length > 1 ? componentEvents.join('\n') : ''}}
|
|
127
|
+
return imports.join('\n');
|
|
128
|
+
};
|
|
129
|
+
const EXTENDED_PATH_REGEX = /^\\\\\?\\/;
|
|
130
|
+
const NON_ASCII_REGEX = /[^\x00-\x80]+/;
|
|
131
|
+
const SLASH_REGEX = /\\/g;
|
|
174
132
|
|
|
175
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Creates an Angular component declaration from formatted Stencil compiler metadata.
|
|
135
|
+
*
|
|
136
|
+
* @param tagName The tag name of the component.
|
|
137
|
+
* @param inputs The inputs of the Stencil component (e.g. ['myInput']).
|
|
138
|
+
* @param outputs The outputs/events of the Stencil component. (e.g. ['myOutput']).
|
|
139
|
+
* @param methods The methods of the Stencil component. (e.g. ['myMethod']).
|
|
140
|
+
* @param includeImportCustomElements Whether to define the component as a custom element.
|
|
141
|
+
* @returns The component declaration as a string.
|
|
142
|
+
*/
|
|
143
|
+
const createAngularComponentDefinition = (tagName, inputs, outputs, methods, includeImportCustomElements = false) => {
|
|
144
|
+
const tagNameAsPascal = dashToPascalCase(tagName);
|
|
145
|
+
const hasInputs = inputs.length > 0;
|
|
146
|
+
const hasOutputs = outputs.length > 0;
|
|
147
|
+
const hasMethods = methods.length > 0;
|
|
148
|
+
// Formats the input strings into comma separated, single quoted values.
|
|
149
|
+
const formattedInputs = formatToQuotedList(inputs);
|
|
150
|
+
// Formats the output strings into comma separated, single quoted values.
|
|
151
|
+
const formattedOutputs = formatToQuotedList(outputs);
|
|
152
|
+
// Formats the method strings into comma separated, single quoted values.
|
|
153
|
+
const formattedMethods = formatToQuotedList(methods);
|
|
154
|
+
const proxyCmpOptions = [];
|
|
155
|
+
if (includeImportCustomElements) {
|
|
156
|
+
const defineCustomElementFn = `define${tagNameAsPascal}`;
|
|
157
|
+
proxyCmpOptions.push(`\n defineCustomElementFn: ${defineCustomElementFn}`);
|
|
158
|
+
}
|
|
159
|
+
if (hasInputs) {
|
|
160
|
+
proxyCmpOptions.push(`\n inputs: [${formattedInputs}]`);
|
|
161
|
+
}
|
|
162
|
+
if (hasMethods) {
|
|
163
|
+
proxyCmpOptions.push(`\n methods: [${formattedMethods}]`);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Notes on the generated output:
|
|
167
|
+
* - We disable @angular-eslint/no-inputs-metadata-property, so that
|
|
168
|
+
* Angular does not complain about the inputs property. The output target
|
|
169
|
+
* uses the inputs property to define the inputs of the component instead of
|
|
170
|
+
* having to use the @Input decorator (and manually define the type and default value).
|
|
171
|
+
*/
|
|
172
|
+
const output = `@ProxyCmp({${proxyCmpOptions.join(',')}\n})
|
|
176
173
|
@Component({
|
|
177
|
-
${
|
|
174
|
+
selector: '${tagName}',
|
|
175
|
+
template: '<ng-content></ng-content>',
|
|
176
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
177
|
+
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
|
178
|
+
inputs: [${formattedInputs}],
|
|
178
179
|
})
|
|
179
|
-
export class ${tagNameAsPascal} {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
lines.push(` constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
|
180
|
+
export class ${tagNameAsPascal} {
|
|
181
|
+
protected el: HTMLElement;
|
|
182
|
+
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
|
183
183
|
c.detach();
|
|
184
|
-
this.el = r.nativeElement
|
|
185
|
-
|
|
186
|
-
|
|
184
|
+
this.el = r.nativeElement;${hasOutputs
|
|
185
|
+
? `
|
|
186
|
+
proxyOutputs(this, this.el, [${formattedOutputs}]);`
|
|
187
|
+
: ''}
|
|
188
|
+
}
|
|
189
|
+
}`;
|
|
190
|
+
return output;
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* Sanitizes and formats the component event type.
|
|
194
|
+
* @param componentClassName The class name of the component (e.g. 'MyComponent')
|
|
195
|
+
* @param event The Stencil component event.
|
|
196
|
+
* @returns The sanitized event type as a string.
|
|
197
|
+
*/
|
|
198
|
+
const formatOutputType = (componentClassName, event) => {
|
|
199
|
+
/**
|
|
200
|
+
* The original attribute contains the original type defined by the devs.
|
|
201
|
+
* This regexp normalizes the reference, by removing linebreaks,
|
|
202
|
+
* replacing consecutive spaces with a single space, and adding a single space after commas.
|
|
203
|
+
*/
|
|
204
|
+
return Object.entries(event.complexType.references)
|
|
205
|
+
.filter(([_, refObject]) => refObject.location === 'local' || refObject.location === 'import')
|
|
206
|
+
.reduce((type, [src, dst]) => {
|
|
207
|
+
const renamedType = `I${componentClassName}${type}`;
|
|
208
|
+
return renamedType
|
|
209
|
+
.replace(new RegExp(`^${src}$`, 'g'), `${dst}`)
|
|
210
|
+
.replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (v, p1, p2) => [p1, dst, p2].join(''));
|
|
211
|
+
}, event.complexType.original
|
|
212
|
+
.replace(/\n/g, ' ')
|
|
213
|
+
.replace(/\s{2,}/g, ' ')
|
|
214
|
+
.replace(/,\s*/g, ', '));
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* Creates a formatted comment block based on the JS doc comment.
|
|
218
|
+
* @param doc The compiler jsdoc.
|
|
219
|
+
* @returns The formatted comment block as a string.
|
|
220
|
+
*/
|
|
221
|
+
const createDocComment = (doc) => {
|
|
222
|
+
if (doc.text.trim().length === 0 && doc.tags.length === 0) {
|
|
223
|
+
return '';
|
|
187
224
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
225
|
+
return `/**
|
|
226
|
+
* ${doc.text}${doc.tags.length > 0 ? ' ' : ''}${doc.tags.map((tag) => `@${tag.name} ${tag.text}`)}
|
|
227
|
+
*/`;
|
|
228
|
+
};
|
|
229
|
+
/**
|
|
230
|
+
* Creates the component interface type definition.
|
|
231
|
+
* @param tagNameAsPascal The tag name as PascalCase.
|
|
232
|
+
* @param events The events to generate the interface properties for.
|
|
233
|
+
* @param componentCorePackage The component core package.
|
|
234
|
+
* @param includeImportCustomElements Whether to include the import for the custom element definition.
|
|
235
|
+
* @param customElementsDir The custom elements directory.
|
|
236
|
+
* @returns The component interface type definition as a string.
|
|
237
|
+
*/
|
|
238
|
+
const createComponentTypeDefinition = (tagNameAsPascal, events, componentCorePackage, includeImportCustomElements = false, customElementsDir) => {
|
|
239
|
+
const typeDefinition = `${createComponentEventTypeImports(tagNameAsPascal, events, {
|
|
240
|
+
componentCorePackage,
|
|
241
|
+
includeImportCustomElements,
|
|
242
|
+
customElementsDir,
|
|
243
|
+
})}
|
|
244
|
+
|
|
245
|
+
export interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {
|
|
246
|
+
${events
|
|
247
|
+
.map((event) => {
|
|
248
|
+
const comment = createDocComment(event.docs);
|
|
249
|
+
return `${comment.length > 0 ? ` ${comment}` : ''}
|
|
250
|
+
${event.name}: EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>;`;
|
|
251
|
+
})
|
|
252
|
+
.join('\n')}
|
|
253
|
+
}`;
|
|
254
|
+
return typeDefinition;
|
|
191
255
|
};
|
|
192
|
-
function getProxyCmp(tagName, includeCustomElement, inputs, methods) {
|
|
193
|
-
const hasInputs = inputs.length > 0;
|
|
194
|
-
const hasMethods = methods.length > 0;
|
|
195
|
-
const proxMeta = [
|
|
196
|
-
`defineCustomElementFn: ${includeCustomElement ? 'define' + dashToPascalCase(tagName) : 'undefined'}`
|
|
197
|
-
];
|
|
198
|
-
if (hasInputs)
|
|
199
|
-
proxMeta.push(`inputs: ['${inputs.join(`', '`)}']`);
|
|
200
|
-
if (hasMethods)
|
|
201
|
-
proxMeta.push(`methods: ['${methods.join(`', '`)}']`);
|
|
202
|
-
return `@ProxyCmp({\n ${proxMeta.join(',\n ')}\n})`;
|
|
203
|
-
}
|
|
204
256
|
|
|
205
257
|
function generateAngularDirectivesFile(compilerCtx, components, outputTarget) {
|
|
206
258
|
// Only create the file if it is defined in the stencil configuration
|
|
207
259
|
if (!outputTarget.directivesArrayFile) {
|
|
208
260
|
return Promise.resolve();
|
|
209
261
|
}
|
|
210
|
-
const proxyPath = relativeImport(outputTarget.directivesArrayFile, outputTarget.
|
|
262
|
+
const proxyPath = relativeImport(outputTarget.directivesArrayFile, outputTarget.proxyDeclarationFile, '.ts');
|
|
211
263
|
const directives = components
|
|
212
264
|
.map((cmpMeta) => dashToPascalCase(cmpMeta.tagName))
|
|
213
265
|
.map((className) => `d.${className}`)
|
|
@@ -223,15 +275,12 @@ export const DIRECTIVES = [
|
|
|
223
275
|
}
|
|
224
276
|
|
|
225
277
|
async function generateValueAccessors(compilerCtx, components, outputTarget, config) {
|
|
226
|
-
if (!Array.isArray(outputTarget.valueAccessorConfigs) ||
|
|
227
|
-
outputTarget.valueAccessorConfigs.length === 0) {
|
|
278
|
+
if (!Array.isArray(outputTarget.valueAccessorConfigs) || outputTarget.valueAccessorConfigs.length === 0) {
|
|
228
279
|
return;
|
|
229
280
|
}
|
|
230
|
-
const targetDir = path__default['default'].dirname(outputTarget.
|
|
281
|
+
const targetDir = path__default['default'].dirname(outputTarget.proxyDeclarationFile);
|
|
231
282
|
const normalizedValueAccessors = outputTarget.valueAccessorConfigs.reduce((allAccessors, va) => {
|
|
232
|
-
const elementSelectors = Array.isArray(va.elementSelectors)
|
|
233
|
-
? va.elementSelectors
|
|
234
|
-
: [va.elementSelectors];
|
|
283
|
+
const elementSelectors = Array.isArray(va.elementSelectors) ? va.elementSelectors : [va.elementSelectors];
|
|
235
284
|
const type = va.type;
|
|
236
285
|
let allElementSelectors = [];
|
|
237
286
|
let allEventTargets = [];
|
|
@@ -280,13 +329,30 @@ const VALUE_ACCESSOR_EVENT = `<VALUE_ACCESSOR_EVENT>`;
|
|
|
280
329
|
const VALUE_ACCESSOR_TARGETATTR = '<VALUE_ACCESSOR_TARGETATTR>';
|
|
281
330
|
const VALUE_ACCESSOR_EVENTTARGETS = ` '(<VALUE_ACCESSOR_EVENT>)': 'handleChangeEvent($event.target.<VALUE_ACCESSOR_TARGETATTR>)'`;
|
|
282
331
|
|
|
332
|
+
/**
|
|
333
|
+
* Creates an Angular module declaration for a component wrapper.
|
|
334
|
+
* @param componentTagName The tag name of the Stencil component.
|
|
335
|
+
* @returns The Angular module declaration as a string.
|
|
336
|
+
*/
|
|
337
|
+
const generateAngularModuleForComponent = (componentTagName) => {
|
|
338
|
+
const tagNameAsPascal = dashToPascalCase(componentTagName);
|
|
339
|
+
const componentClassName = `${tagNameAsPascal}`;
|
|
340
|
+
const moduleClassName = `${tagNameAsPascal}Module`;
|
|
341
|
+
const moduleDefinition = `@NgModule({
|
|
342
|
+
declarations: [${componentClassName}],
|
|
343
|
+
exports: [${componentClassName}]
|
|
344
|
+
})
|
|
345
|
+
export class ${moduleClassName} { }`;
|
|
346
|
+
return moduleDefinition;
|
|
347
|
+
};
|
|
348
|
+
|
|
283
349
|
async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
|
|
284
350
|
const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
|
|
285
351
|
const rootDir = config.rootDir;
|
|
286
352
|
const pkgData = await readPackageJson(config, rootDir);
|
|
287
353
|
const finalText = generateProxies(filteredComponents, pkgData, outputTarget, config.rootDir);
|
|
288
354
|
await Promise.all([
|
|
289
|
-
compilerCtx.fs.writeFile(outputTarget.
|
|
355
|
+
compilerCtx.fs.writeFile(outputTarget.proxyDeclarationFile, finalText),
|
|
290
356
|
copyResources$1(config, outputTarget),
|
|
291
357
|
generateAngularDirectivesFile(compilerCtx, filteredComponents, outputTarget),
|
|
292
358
|
generateValueAccessors(compilerCtx, filteredComponents, outputTarget, config),
|
|
@@ -300,7 +366,7 @@ async function copyResources$1(config, outputTarget) {
|
|
|
300
366
|
throw new Error('stencil is not properly initialized at this step. Notify the developer');
|
|
301
367
|
}
|
|
302
368
|
const srcDirectory = path__default['default'].join(__dirname, '..', 'angular-component-lib');
|
|
303
|
-
const destDirectory = path__default['default'].join(path__default['default'].dirname(outputTarget.
|
|
369
|
+
const destDirectory = path__default['default'].join(path__default['default'].dirname(outputTarget.proxyDeclarationFile), 'angular-component-lib');
|
|
304
370
|
return config.sys.copy([
|
|
305
371
|
{
|
|
306
372
|
src: srcDirectory,
|
|
@@ -311,14 +377,34 @@ async function copyResources$1(config, outputTarget) {
|
|
|
311
377
|
], srcDirectory);
|
|
312
378
|
}
|
|
313
379
|
function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
380
|
+
var _a;
|
|
314
381
|
const distTypesDir = path__default['default'].dirname(pkgData.types);
|
|
315
382
|
const dtsFilePath = path__default['default'].join(rootDir, distTypesDir, GENERATED_DTS);
|
|
316
|
-
const componentsTypeFile = relativeImport(outputTarget.
|
|
383
|
+
const componentsTypeFile = relativeImport(outputTarget.proxyDeclarationFile, dtsFilePath, '.d.ts');
|
|
384
|
+
const createSingleComponentAngularModules = (_a = outputTarget.createSingleComponentAngularModules) !== null && _a !== void 0 ? _a : false;
|
|
385
|
+
/**
|
|
386
|
+
* The collection of named imports from @angular/core.
|
|
387
|
+
*/
|
|
388
|
+
const angularCoreImports = [
|
|
389
|
+
'ChangeDetectionStrategy',
|
|
390
|
+
'ChangeDetectorRef',
|
|
391
|
+
'Component',
|
|
392
|
+
'ElementRef',
|
|
393
|
+
'EventEmitter',
|
|
394
|
+
'NgZone',
|
|
395
|
+
];
|
|
396
|
+
/**
|
|
397
|
+
* The collection of named imports from the angular-component-lib/utils.
|
|
398
|
+
*/
|
|
399
|
+
const componentLibImports = ['ProxyCmp', 'proxyOutputs'];
|
|
400
|
+
if (createSingleComponentAngularModules) {
|
|
401
|
+
angularCoreImports.push('NgModule');
|
|
402
|
+
}
|
|
317
403
|
const imports = `/* tslint:disable */
|
|
318
404
|
/* auto-generated angular directive proxies */
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
405
|
+
${createImportStatement(angularCoreImports, '@angular/core')}
|
|
406
|
+
|
|
407
|
+
${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n`;
|
|
322
408
|
/**
|
|
323
409
|
* Generate JSX import type from correct location.
|
|
324
410
|
* When using custom elements build, we need to import from
|
|
@@ -326,21 +412,15 @@ import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';\n`;
|
|
|
326
412
|
* otherwise we risk bundlers pulling in lazy loaded imports.
|
|
327
413
|
*/
|
|
328
414
|
const generateTypeImports = () => {
|
|
415
|
+
let importLocation = outputTarget.componentCorePackage
|
|
416
|
+
? normalizePath(outputTarget.componentCorePackage)
|
|
417
|
+
: normalizePath(componentsTypeFile);
|
|
418
|
+
importLocation += outputTarget.includeImportCustomElements
|
|
419
|
+
? `/${outputTarget.customElementsDir || 'components'}`
|
|
420
|
+
: '';
|
|
329
421
|
return `import ${outputTarget.includeImportCustomElements ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
|
|
330
422
|
};
|
|
331
|
-
|
|
332
|
-
* Components that have custom events have uniquely generated interfaces as of
|
|
333
|
-
* Stencil 2.16.0. We import these interfaces so they can be used in the generate
|
|
334
|
-
* functions for the @Output() events.
|
|
335
|
-
*/
|
|
336
|
-
const customEventInterfaces = components.filter(c => c.events.filter(e => !e.internal).length > 0).map(c => {
|
|
337
|
-
return formatCustomEventInterfaceName(c.tagName);
|
|
338
|
-
});
|
|
339
|
-
const customEventImports = `import type { ${customEventInterfaces.join(', ')} } from '${importLocation}';\n`;
|
|
340
|
-
const typeImports = [
|
|
341
|
-
generateTypeImports(),
|
|
342
|
-
customEventImports,
|
|
343
|
-
];
|
|
423
|
+
const typeImports = generateTypeImports();
|
|
344
424
|
let sourceImports = '';
|
|
345
425
|
/**
|
|
346
426
|
* Build an array of Custom Elements build imports and namespace them
|
|
@@ -349,31 +429,54 @@ import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';\n`;
|
|
|
349
429
|
* IonButton React Component that takes in the Web Component as a parameter.
|
|
350
430
|
*/
|
|
351
431
|
if (outputTarget.includeImportCustomElements && outputTarget.componentCorePackage !== undefined) {
|
|
352
|
-
const cmpImports = components.map(component => {
|
|
432
|
+
const cmpImports = components.map((component) => {
|
|
353
433
|
const pascalImport = dashToPascalCase(component.tagName);
|
|
354
|
-
return `import { defineCustomElement as define${pascalImport} } from '${normalizePath(outputTarget.componentCorePackage)}/${outputTarget.customElementsDir ||
|
|
355
|
-
'components'}/${component.tagName}.js';`;
|
|
434
|
+
return `import { defineCustomElement as define${pascalImport} } from '${normalizePath(outputTarget.componentCorePackage)}/${outputTarget.customElementsDir || 'components'}/${component.tagName}.js';`;
|
|
356
435
|
});
|
|
357
436
|
sourceImports = cmpImports.join('\n');
|
|
358
437
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
438
|
+
if (createSingleComponentAngularModules) {
|
|
439
|
+
// Generating Angular modules is only supported in the dist-custom-elements build
|
|
440
|
+
if (!outputTarget.includeImportCustomElements) {
|
|
441
|
+
throw new Error('Generating single component Angular modules requires the "includeImportCustomElements" option to be set to true.');
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const proxyFileOutput = [];
|
|
445
|
+
const filterInternalProps = (prop) => !prop.internal;
|
|
446
|
+
const mapPropName = (prop) => prop.name;
|
|
447
|
+
const { includeImportCustomElements, componentCorePackage, customElementsDir } = outputTarget;
|
|
448
|
+
for (let cmpMeta of components) {
|
|
449
|
+
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
450
|
+
const inputs = [];
|
|
451
|
+
if (cmpMeta.properties) {
|
|
452
|
+
inputs.push(...cmpMeta.properties.filter(filterInternalProps).map(mapPropName));
|
|
453
|
+
}
|
|
454
|
+
if (cmpMeta.virtualProperties) {
|
|
455
|
+
inputs.push(...cmpMeta.virtualProperties.map(mapPropName));
|
|
456
|
+
}
|
|
457
|
+
inputs.sort();
|
|
458
|
+
const outputs = [];
|
|
459
|
+
if (cmpMeta.events) {
|
|
460
|
+
outputs.push(...cmpMeta.events.filter(filterInternalProps).map(mapPropName));
|
|
461
|
+
}
|
|
462
|
+
const methods = [];
|
|
463
|
+
if (cmpMeta.methods) {
|
|
464
|
+
methods.push(...cmpMeta.methods.filter(filterInternalProps).map(mapPropName));
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* For each component, we need to generate:
|
|
468
|
+
* 1. The @Component decorated class
|
|
469
|
+
* 2. Optionally the @NgModule decorated class (if createSingleComponentAngularModules is true)
|
|
470
|
+
* 3. The component interface (using declaration merging for types).
|
|
471
|
+
*/
|
|
472
|
+
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName, inputs, outputs, methods, includeImportCustomElements);
|
|
473
|
+
const moduleDefinition = generateAngularModuleForComponent(cmpMeta.tagName);
|
|
474
|
+
const componentTypeDefinition = createComponentTypeDefinition(tagNameAsPascal, cmpMeta.events, componentCorePackage, includeImportCustomElements, customElementsDir);
|
|
475
|
+
proxyFileOutput.push(componentDefinition, '\n', createSingleComponentAngularModules ? moduleDefinition : '', '\n', componentTypeDefinition, '\n');
|
|
476
|
+
}
|
|
477
|
+
const final = [imports, typeImports, sourceImports, ...proxyFileOutput];
|
|
367
478
|
return final.join('\n') + '\n';
|
|
368
479
|
}
|
|
369
|
-
/**
|
|
370
|
-
* Returns the path to import the file from.
|
|
371
|
-
*/
|
|
372
|
-
const getImportPackageName = (outputTarget, componentsTypeFile) => {
|
|
373
|
-
let importLocation = outputTarget.componentCorePackage ? normalizePath(outputTarget.componentCorePackage) : normalizePath(componentsTypeFile);
|
|
374
|
-
importLocation += outputTarget.includeImportCustomElements ? `/${outputTarget.customElementsDir || 'components'}` : '';
|
|
375
|
-
return importLocation;
|
|
376
|
-
};
|
|
377
480
|
const GENERATED_DTS = 'components.d.ts';
|
|
378
481
|
const IMPORT_TYPES = 'Components';
|
|
379
482
|
|
|
@@ -390,15 +493,18 @@ const angularOutputTarget = (outputTarget) => ({
|
|
|
390
493
|
},
|
|
391
494
|
});
|
|
392
495
|
function normalizeOutputTarget(config, outputTarget) {
|
|
393
|
-
const results = Object.assign(Object.assign({}, outputTarget), { excludeComponents: outputTarget.excludeComponents || [],
|
|
496
|
+
const results = Object.assign(Object.assign({}, outputTarget), { excludeComponents: outputTarget.excludeComponents || [], valueAccessorConfigs: outputTarget.valueAccessorConfigs || [] });
|
|
394
497
|
if (config.rootDir == null) {
|
|
395
498
|
throw new Error('rootDir is not set and it should be set by stencil itself');
|
|
396
499
|
}
|
|
397
|
-
if (outputTarget.directivesProxyFile
|
|
398
|
-
throw new Error('directivesProxyFile
|
|
500
|
+
if (outputTarget.directivesProxyFile !== undefined) {
|
|
501
|
+
throw new Error('directivesProxyFile has been removed. Use proxyDeclarationFile instead.');
|
|
502
|
+
}
|
|
503
|
+
if (outputTarget.proxyDeclarationFile == null) {
|
|
504
|
+
throw new Error('proxyDeclarationFile is required. Please set it in the Stencil config.');
|
|
399
505
|
}
|
|
400
|
-
if (outputTarget.
|
|
401
|
-
results.
|
|
506
|
+
if (outputTarget.proxyDeclarationFile && !path__default['default'].isAbsolute(outputTarget.proxyDeclarationFile)) {
|
|
507
|
+
results.proxyDeclarationFile = normalizePath(path__default['default'].join(config.rootDir, outputTarget.proxyDeclarationFile));
|
|
402
508
|
}
|
|
403
509
|
if (outputTarget.directivesArrayFile && !path__default['default'].isAbsolute(outputTarget.directivesArrayFile)) {
|
|
404
510
|
results.directivesArrayFile = normalizePath(path__default['default'].join(config.rootDir, outputTarget.directivesArrayFile));
|