@stencil/angular-output-target 0.4.0 → 0.5.1-dev.11685564559.1b0b6e89
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 +49 -1
- package/angular-component-lib/utils.ts +9 -15
- package/dist/generate-angular-component.d.ts +22 -2
- package/dist/generate-angular-component.js +127 -94
- package/dist/generate-angular-modules.d.ts +6 -0
- package/dist/generate-angular-modules.js +17 -0
- package/dist/generate-value-accessors.js +2 -5
- package/dist/index.cjs.js +269 -116
- package/dist/index.js +269 -116
- package/dist/output-angular.js +79 -17
- package/dist/plugin.d.ts +1 -1
- package/dist/plugin.js +4 -4
- package/dist/types.d.ts +15 -2
- package/dist/utils.d.ts +27 -1
- package/dist/utils.js +45 -0
- package/package.json +10 -3
package/dist/index.cjs.js
CHANGED
|
@@ -81,110 +81,188 @@ async function readPackageJson(config, rootDir) {
|
|
|
81
81
|
}
|
|
82
82
|
return pkgData;
|
|
83
83
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
`selector: \'${cmpMeta.tagName}\'`,
|
|
101
|
-
`changeDetection: ChangeDetectionStrategy.OnPush`,
|
|
102
|
-
`template: '<ng-content></ng-content>'`,
|
|
103
|
-
];
|
|
104
|
-
if (inputs.length > 0) {
|
|
105
|
-
directiveOpts.push(`inputs: ['${inputs.join(`', '`)}']`);
|
|
84
|
+
/**
|
|
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.
|
|
94
|
+
*
|
|
95
|
+
* @returns The import statement as a string.
|
|
96
|
+
*/
|
|
97
|
+
const createImportStatement = (imports, module) => {
|
|
98
|
+
if (imports.length === 0) {
|
|
99
|
+
return '';
|
|
106
100
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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]) => {
|
|
116
117
|
if (refObject.location === 'local' || refObject.location === 'import') {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
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);
|
|
122
123
|
}
|
|
123
|
-
outputsInterface.add(`import type { ${reference} as ${remappedReference} } from '${importLocation}';`);
|
|
124
124
|
}
|
|
125
125
|
});
|
|
126
126
|
});
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
componentEvents.push(` /**
|
|
133
|
-
* ${output.docs.text} ${output.docs.tags.map((tag) => `@${tag.name} ${tag.text}`)}
|
|
134
|
-
*/`);
|
|
135
|
-
/**
|
|
136
|
-
* The original attribute contains the original type defined by the devs.
|
|
137
|
-
* This regexp normalizes the reference, by removing linebreaks,
|
|
138
|
-
* replacing consecutive spaces with a single space, and adding a single space after commas.
|
|
139
|
-
**/
|
|
140
|
-
const outputTypeRemapped = Object.entries(outputReferenceRemap).reduce((type, [src, dst]) => {
|
|
141
|
-
return type
|
|
142
|
-
.replace(new RegExp(`^${src}$`, 'g'), `${dst}`)
|
|
143
|
-
.replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (v, p1, p2) => [p1, dst, p2].join(''));
|
|
144
|
-
}, output.complexType.original
|
|
145
|
-
.replace(/\n/g, ' ')
|
|
146
|
-
.replace(/\s{2,}/g, ' ')
|
|
147
|
-
.replace(/,\s*/g, ', '));
|
|
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
|
-
}
|
|
153
|
-
});
|
|
154
|
-
const lines = [
|
|
155
|
-
'',
|
|
156
|
-
`${[...outputsInterface].join('\n')}
|
|
157
|
-
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;
|
|
158
132
|
|
|
159
|
-
|
|
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})
|
|
160
173
|
@Component({
|
|
161
|
-
${
|
|
174
|
+
selector: '${tagName}',
|
|
175
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
176
|
+
template: '<ng-content></ng-content>',
|
|
177
|
+
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
|
178
|
+
inputs: [${formattedInputs}],
|
|
162
179
|
})
|
|
163
|
-
export class ${tagNameAsPascal} {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
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) {
|
|
167
183
|
c.detach();
|
|
168
|
-
this.el = r.nativeElement
|
|
169
|
-
|
|
170
|
-
|
|
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
|
+
// Capture all instances of the `src` field surrounded by non-word characters on each side and join them.
|
|
211
|
+
.replace(new RegExp(`([^\\w])${src}([^\\w])`, 'g'), (v, p1, p2) => [p1, dst, p2].join('')));
|
|
212
|
+
}, event.complexType.original
|
|
213
|
+
.replace(/\n/g, ' ')
|
|
214
|
+
.replace(/\s{2,}/g, ' ')
|
|
215
|
+
.replace(/,\s*/g, ', '));
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* Creates a formatted comment block based on the JS doc comment.
|
|
219
|
+
* @param doc The compiler jsdoc.
|
|
220
|
+
* @returns The formatted comment block as a string.
|
|
221
|
+
*/
|
|
222
|
+
const createDocComment = (doc) => {
|
|
223
|
+
if (doc.text.trim().length === 0 && doc.tags.length === 0) {
|
|
224
|
+
return '';
|
|
171
225
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
226
|
+
return `/**
|
|
227
|
+
* ${doc.text}${doc.tags.length > 0 ? ' ' : ''}${doc.tags.map((tag) => `@${tag.name} ${tag.text}`)}
|
|
228
|
+
*/`;
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Creates the component interface type definition.
|
|
232
|
+
* @param tagNameAsPascal The tag name as PascalCase.
|
|
233
|
+
* @param events The events to generate the interface properties for.
|
|
234
|
+
* @param componentCorePackage The component core package.
|
|
235
|
+
* @param includeImportCustomElements Whether to include the import for the custom element definition.
|
|
236
|
+
* @param customElementsDir The custom elements directory.
|
|
237
|
+
* @returns The component interface type definition as a string.
|
|
238
|
+
*/
|
|
239
|
+
const createComponentTypeDefinition = (tagNameAsPascal, events, componentCorePackage, includeImportCustomElements = false, customElementsDir) => {
|
|
240
|
+
const publicEvents = events.filter((ev) => !ev.internal);
|
|
241
|
+
const eventTypeImports = createComponentEventTypeImports(tagNameAsPascal, publicEvents, {
|
|
242
|
+
componentCorePackage,
|
|
243
|
+
includeImportCustomElements,
|
|
244
|
+
customElementsDir,
|
|
245
|
+
});
|
|
246
|
+
const eventTypes = publicEvents.map((event) => {
|
|
247
|
+
const comment = createDocComment(event.docs);
|
|
248
|
+
let eventName = event.name;
|
|
249
|
+
if (event.name.includes('-')) {
|
|
250
|
+
// If an event name includes a dash, we need to wrap it in quotes.
|
|
251
|
+
// https://github.com/ionic-team/stencil-ds-output-targets/issues/212
|
|
252
|
+
eventName = `'${event.name}'`;
|
|
253
|
+
}
|
|
254
|
+
return `${comment.length > 0 ? ` ${comment}` : ''}
|
|
255
|
+
${eventName}: EventEmitter<CustomEvent<${formatOutputType(tagNameAsPascal, event)}>>;`;
|
|
256
|
+
});
|
|
257
|
+
const interfaceDeclaration = `export declare interface ${tagNameAsPascal} extends Components.${tagNameAsPascal} {`;
|
|
258
|
+
const typeDefinition = (eventTypeImports.length > 0 ? `${eventTypeImports + '\n\n'}` : '') +
|
|
259
|
+
`${interfaceDeclaration}${eventTypes.length === 0
|
|
260
|
+
? '}'
|
|
261
|
+
: `
|
|
262
|
+
${eventTypes.join('\n')}
|
|
263
|
+
}`}`;
|
|
264
|
+
return typeDefinition;
|
|
175
265
|
};
|
|
176
|
-
function getProxyCmp(tagName, includeCustomElement, inputs, methods) {
|
|
177
|
-
const hasInputs = inputs.length > 0;
|
|
178
|
-
const hasMethods = methods.length > 0;
|
|
179
|
-
const proxMeta = [
|
|
180
|
-
`defineCustomElementFn: ${includeCustomElement ? 'define' + dashToPascalCase(tagName) : 'undefined'}`
|
|
181
|
-
];
|
|
182
|
-
if (hasInputs)
|
|
183
|
-
proxMeta.push(`inputs: ['${inputs.join(`', '`)}']`);
|
|
184
|
-
if (hasMethods)
|
|
185
|
-
proxMeta.push(`methods: ['${methods.join(`', '`)}']`);
|
|
186
|
-
return `@ProxyCmp({\n ${proxMeta.join(',\n ')}\n})`;
|
|
187
|
-
}
|
|
188
266
|
|
|
189
267
|
function generateAngularDirectivesFile(compilerCtx, components, outputTarget) {
|
|
190
268
|
// Only create the file if it is defined in the stencil configuration
|
|
@@ -207,15 +285,12 @@ export const DIRECTIVES = [
|
|
|
207
285
|
}
|
|
208
286
|
|
|
209
287
|
async function generateValueAccessors(compilerCtx, components, outputTarget, config) {
|
|
210
|
-
if (!Array.isArray(outputTarget.valueAccessorConfigs) ||
|
|
211
|
-
outputTarget.valueAccessorConfigs.length === 0) {
|
|
288
|
+
if (!Array.isArray(outputTarget.valueAccessorConfigs) || outputTarget.valueAccessorConfigs.length === 0) {
|
|
212
289
|
return;
|
|
213
290
|
}
|
|
214
291
|
const targetDir = path__default['default'].dirname(outputTarget.directivesProxyFile);
|
|
215
292
|
const normalizedValueAccessors = outputTarget.valueAccessorConfigs.reduce((allAccessors, va) => {
|
|
216
|
-
const elementSelectors = Array.isArray(va.elementSelectors)
|
|
217
|
-
? va.elementSelectors
|
|
218
|
-
: [va.elementSelectors];
|
|
293
|
+
const elementSelectors = Array.isArray(va.elementSelectors) ? va.elementSelectors : [va.elementSelectors];
|
|
219
294
|
const type = va.type;
|
|
220
295
|
let allElementSelectors = [];
|
|
221
296
|
let allEventTargets = [];
|
|
@@ -264,6 +339,23 @@ const VALUE_ACCESSOR_EVENT = `<VALUE_ACCESSOR_EVENT>`;
|
|
|
264
339
|
const VALUE_ACCESSOR_TARGETATTR = '<VALUE_ACCESSOR_TARGETATTR>';
|
|
265
340
|
const VALUE_ACCESSOR_EVENTTARGETS = ` '(<VALUE_ACCESSOR_EVENT>)': 'handleChangeEvent($event.target.<VALUE_ACCESSOR_TARGETATTR>)'`;
|
|
266
341
|
|
|
342
|
+
/**
|
|
343
|
+
* Creates an Angular module declaration for a component wrapper.
|
|
344
|
+
* @param componentTagName The tag name of the Stencil component.
|
|
345
|
+
* @returns The Angular module declaration as a string.
|
|
346
|
+
*/
|
|
347
|
+
const generateAngularModuleForComponent = (componentTagName) => {
|
|
348
|
+
const tagNameAsPascal = dashToPascalCase(componentTagName);
|
|
349
|
+
const componentClassName = `${tagNameAsPascal}`;
|
|
350
|
+
const moduleClassName = `${tagNameAsPascal}Module`;
|
|
351
|
+
const moduleDefinition = `@NgModule({
|
|
352
|
+
declarations: [${componentClassName}],
|
|
353
|
+
exports: [${componentClassName}]
|
|
354
|
+
})
|
|
355
|
+
export class ${moduleClassName} { }`;
|
|
356
|
+
return moduleDefinition;
|
|
357
|
+
};
|
|
358
|
+
|
|
267
359
|
async function angularDirectiveProxyOutput(compilerCtx, outputTarget, components, config) {
|
|
268
360
|
const filteredComponents = getFilteredComponents(outputTarget.excludeComponents, components);
|
|
269
361
|
const rootDir = config.rootDir;
|
|
@@ -295,13 +387,35 @@ async function copyResources$1(config, outputTarget) {
|
|
|
295
387
|
], srcDirectory);
|
|
296
388
|
}
|
|
297
389
|
function generateProxies(components, pkgData, outputTarget, rootDir) {
|
|
390
|
+
var _a;
|
|
298
391
|
const distTypesDir = path__default['default'].dirname(pkgData.types);
|
|
299
392
|
const dtsFilePath = path__default['default'].join(rootDir, distTypesDir, GENERATED_DTS);
|
|
300
393
|
const componentsTypeFile = relativeImport(outputTarget.directivesProxyFile, dtsFilePath, '.d.ts');
|
|
394
|
+
const includeSingleComponentAngularModules = (_a = outputTarget.includeSingleComponentAngularModules) !== null && _a !== void 0 ? _a : false;
|
|
395
|
+
const includeOutputImports = components.some((component) => component.events.some((event) => !event.internal));
|
|
396
|
+
/**
|
|
397
|
+
* The collection of named imports from @angular/core.
|
|
398
|
+
*/
|
|
399
|
+
const angularCoreImports = ['ChangeDetectionStrategy', 'ChangeDetectorRef', 'Component', 'ElementRef'];
|
|
400
|
+
if (includeOutputImports) {
|
|
401
|
+
angularCoreImports.push('EventEmitter');
|
|
402
|
+
}
|
|
403
|
+
angularCoreImports.push('NgZone');
|
|
404
|
+
/**
|
|
405
|
+
* The collection of named imports from the angular-component-lib/utils.
|
|
406
|
+
*/
|
|
407
|
+
const componentLibImports = ['ProxyCmp'];
|
|
408
|
+
if (includeOutputImports) {
|
|
409
|
+
componentLibImports.push('proxyOutputs');
|
|
410
|
+
}
|
|
411
|
+
if (includeSingleComponentAngularModules) {
|
|
412
|
+
angularCoreImports.push('NgModule');
|
|
413
|
+
}
|
|
301
414
|
const imports = `/* tslint:disable */
|
|
302
415
|
/* auto-generated angular directive proxies */
|
|
303
|
-
|
|
304
|
-
|
|
416
|
+
${createImportStatement(angularCoreImports, '@angular/core')}
|
|
417
|
+
|
|
418
|
+
${createImportStatement(componentLibImports, './angular-component-lib/utils')}\n`;
|
|
305
419
|
/**
|
|
306
420
|
* Generate JSX import type from correct location.
|
|
307
421
|
* When using custom elements build, we need to import from
|
|
@@ -309,8 +423,12 @@ import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';\n`;
|
|
|
309
423
|
* otherwise we risk bundlers pulling in lazy loaded imports.
|
|
310
424
|
*/
|
|
311
425
|
const generateTypeImports = () => {
|
|
312
|
-
let importLocation = outputTarget.componentCorePackage
|
|
313
|
-
|
|
426
|
+
let importLocation = outputTarget.componentCorePackage
|
|
427
|
+
? normalizePath(outputTarget.componentCorePackage)
|
|
428
|
+
: normalizePath(componentsTypeFile);
|
|
429
|
+
importLocation += outputTarget.includeImportCustomElements
|
|
430
|
+
? `/${outputTarget.customElementsDir || 'components'}`
|
|
431
|
+
: '';
|
|
314
432
|
return `import ${outputTarget.includeImportCustomElements ? 'type ' : ''}{ ${IMPORT_TYPES} } from '${importLocation}';\n`;
|
|
315
433
|
};
|
|
316
434
|
const typeImports = generateTypeImports();
|
|
@@ -322,21 +440,56 @@ import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';\n`;
|
|
|
322
440
|
* IonButton React Component that takes in the Web Component as a parameter.
|
|
323
441
|
*/
|
|
324
442
|
if (outputTarget.includeImportCustomElements && outputTarget.componentCorePackage !== undefined) {
|
|
325
|
-
const cmpImports = components.map(component => {
|
|
443
|
+
const cmpImports = components.map((component) => {
|
|
326
444
|
const pascalImport = dashToPascalCase(component.tagName);
|
|
327
|
-
return `import { defineCustomElement as define${pascalImport} } from '${normalizePath(outputTarget.componentCorePackage)}/${outputTarget.customElementsDir ||
|
|
328
|
-
'components'}/${component.tagName}.js';`;
|
|
445
|
+
return `import { defineCustomElement as define${pascalImport} } from '${normalizePath(outputTarget.componentCorePackage)}/${outputTarget.customElementsDir || 'components'}/${component.tagName}.js';`;
|
|
329
446
|
});
|
|
330
447
|
sourceImports = cmpImports.join('\n');
|
|
331
448
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
449
|
+
if (includeSingleComponentAngularModules) {
|
|
450
|
+
// Generating Angular modules is only supported in the dist-custom-elements build
|
|
451
|
+
if (!outputTarget.includeImportCustomElements) {
|
|
452
|
+
throw new Error('Generating single component Angular modules requires the "includeImportCustomElements" option to be set to true.');
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
const proxyFileOutput = [];
|
|
456
|
+
const filterInternalProps = (prop) => !prop.internal;
|
|
457
|
+
const mapPropName = (prop) => prop.name;
|
|
458
|
+
const { includeImportCustomElements, componentCorePackage, customElementsDir } = outputTarget;
|
|
459
|
+
for (let cmpMeta of components) {
|
|
460
|
+
const tagNameAsPascal = dashToPascalCase(cmpMeta.tagName);
|
|
461
|
+
const inputs = [];
|
|
462
|
+
if (cmpMeta.properties) {
|
|
463
|
+
inputs.push(...cmpMeta.properties.filter(filterInternalProps).map(mapPropName));
|
|
464
|
+
}
|
|
465
|
+
if (cmpMeta.virtualProperties) {
|
|
466
|
+
inputs.push(...cmpMeta.virtualProperties.map(mapPropName));
|
|
467
|
+
}
|
|
468
|
+
inputs.sort();
|
|
469
|
+
const outputs = [];
|
|
470
|
+
if (cmpMeta.events) {
|
|
471
|
+
outputs.push(...cmpMeta.events.filter(filterInternalProps).map(mapPropName));
|
|
472
|
+
}
|
|
473
|
+
const methods = [];
|
|
474
|
+
if (cmpMeta.methods) {
|
|
475
|
+
methods.push(...cmpMeta.methods.filter(filterInternalProps).map(mapPropName));
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* For each component, we need to generate:
|
|
479
|
+
* 1. The @Component decorated class
|
|
480
|
+
* 2. Optionally the @NgModule decorated class (if includeSingleComponentAngularModules is true)
|
|
481
|
+
* 3. The component interface (using declaration merging for types).
|
|
482
|
+
*/
|
|
483
|
+
const componentDefinition = createAngularComponentDefinition(cmpMeta.tagName, inputs, outputs, methods, includeImportCustomElements);
|
|
484
|
+
const moduleDefinition = generateAngularModuleForComponent(cmpMeta.tagName);
|
|
485
|
+
const componentTypeDefinition = createComponentTypeDefinition(tagNameAsPascal, cmpMeta.events, componentCorePackage, includeImportCustomElements, customElementsDir);
|
|
486
|
+
proxyFileOutput.push(componentDefinition, '\n');
|
|
487
|
+
if (includeSingleComponentAngularModules) {
|
|
488
|
+
proxyFileOutput.push(moduleDefinition, '\n');
|
|
489
|
+
}
|
|
490
|
+
proxyFileOutput.push(componentTypeDefinition, '\n');
|
|
491
|
+
}
|
|
492
|
+
const final = [imports, typeImports, sourceImports, ...proxyFileOutput];
|
|
340
493
|
return final.join('\n') + '\n';
|
|
341
494
|
}
|
|
342
495
|
const GENERATED_DTS = 'components.d.ts';
|
|
@@ -355,12 +508,12 @@ const angularOutputTarget = (outputTarget) => ({
|
|
|
355
508
|
},
|
|
356
509
|
});
|
|
357
510
|
function normalizeOutputTarget(config, outputTarget) {
|
|
358
|
-
const results = Object.assign(Object.assign({}, outputTarget), { excludeComponents: outputTarget.excludeComponents || [],
|
|
511
|
+
const results = Object.assign(Object.assign({}, outputTarget), { excludeComponents: outputTarget.excludeComponents || [], valueAccessorConfigs: outputTarget.valueAccessorConfigs || [] });
|
|
359
512
|
if (config.rootDir == null) {
|
|
360
513
|
throw new Error('rootDir is not set and it should be set by stencil itself');
|
|
361
514
|
}
|
|
362
515
|
if (outputTarget.directivesProxyFile == null) {
|
|
363
|
-
throw new Error('directivesProxyFile is required');
|
|
516
|
+
throw new Error('directivesProxyFile is required. Please set it in the Stencil config.');
|
|
364
517
|
}
|
|
365
518
|
if (outputTarget.directivesProxyFile && !path__default['default'].isAbsolute(outputTarget.directivesProxyFile)) {
|
|
366
519
|
results.directivesProxyFile = normalizePath(path__default['default'].join(config.rootDir, outputTarget.directivesProxyFile));
|
|
@@ -368,8 +521,8 @@ function normalizeOutputTarget(config, outputTarget) {
|
|
|
368
521
|
if (outputTarget.directivesArrayFile && !path__default['default'].isAbsolute(outputTarget.directivesArrayFile)) {
|
|
369
522
|
results.directivesArrayFile = normalizePath(path__default['default'].join(config.rootDir, outputTarget.directivesArrayFile));
|
|
370
523
|
}
|
|
371
|
-
if (outputTarget.
|
|
372
|
-
|
|
524
|
+
if (outputTarget.includeSingleComponentAngularModules !== undefined) {
|
|
525
|
+
console.warn('**Experimental**: includeSingleComponentAngularModules is a developer preview feature and may change or be removed in the future.');
|
|
373
526
|
}
|
|
374
527
|
return results;
|
|
375
528
|
}
|